home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Networking / nmap-5.00-setup.exe / scripts / whois.nse < prev   
Text File  |  2009-07-06  |  90KB  |  2,189 lines

  1. description = [[
  2. Queries the WHOIS services of Regional Internet Registries (RIR) and attempts to retrieve information about the IP Address
  3. Assignment which contains the Target IP Address.
  4.  
  5. The fields displayed contain information about the assignment and the organisation responsible for managing the address
  6. space. When output verbosity is requested on the Nmap command line (<code>-v</code>) extra information about the assignment will
  7. be displayed.
  8.  
  9. To determine which of the RIRs to query for a given Target IP Address this script utilises Assignments Data hosted by IANA.
  10. The data is cached locally and then parsed for use as a lookup table.  The locally cached files are refreshed periodically
  11. to help ensure the data is current.  If, for any reason, these files are not available to the script then a default sequence
  12. of Whois services are queried in turn until: the desired record is found; or a referral to another (defined) Whois service is
  13. found; or until the sequence is exhausted without finding either a referral or the desired record.
  14.  
  15. The script will recognize a referral to another Whois service if that service is defined in the script and will continue by
  16. sending a query to the referred service.  A record is assumed to be the desired one if it does not contain a referral.
  17.  
  18. To reduce the number unnecessary queries sent to Whois services a record cache is employed and the entries in the cache can be
  19. applied to any targets within the range of addresses represented in the record.
  20.  
  21. In certain circumstances, the ability to cache responses prevents the discovery of other, smaller IP address assignments
  22. applicable to the target because a cached response is accepted in preference to sending a Whois query.  When it is important
  23. to ensure that the most accurate information about the IP address assignment is retrieved the script argument <code>whodb</code>
  24. should be used with a value of <code>"nocache"</code> (see script arguments).  This reduces the range of addresses that may use a
  25. cached record to a size that helps ensure that smaller assignments will be discovered.  This option should be used with caution
  26. due to the potential to send large numbers of whois queries and possibly be banned from using the services.
  27.  
  28. In using this script your IP address will be sent to iana.org. Additionally
  29. your address and the address of the target of the scan will be sent to one of
  30. the RIRs.
  31. ]]
  32.  
  33. ---
  34. -- @args whodb Takes any of the following values, which may be combined:
  35. -- * <code>whodb=nofile</code> Prevent the use of IANA assignments data and instead query the default services.
  36. -- * <code>whodb=nofollow</code> Ignore referrals and instead display the first record obtained.
  37. -- * <code>whodb=nocache</code> Prevent the acceptance of records in the cache when they apply to large ranges of addresses.
  38. -- * <code>whodb=[service-ids]</code> Redefine the default services to query.  Implies <code>nofile</code>.
  39. -- @usage
  40. -- # Basic usage:
  41. -- nmap target --script whois
  42. --
  43. -- # To prevent the use of IANA assignments data supply the nofile value
  44. -- # to the whodb argument:
  45. -- nmap target --script whois --script-args whodb=nofile
  46. -- nmap target --script whois --script-args whois={whodb=nofile}
  47. --
  48. -- # Supplying a sequence of whois services will also prevent the use of
  49. -- # IANA assignments data and override the default sequence:
  50. -- nmap target --script whois --script-args whodb=arin+ripe+afrinic
  51. -- nmap target --script whois --script-args whois={whodb=apnic*lacnic}
  52. -- # The order in which the services are supplied is the order in which
  53. -- # they will be queried. (N.B. commas or semi-colons should not be
  54. -- # used to delimit argument values.)
  55. --
  56. -- # To return the first record obtained even if it contains a referral
  57. -- # to another service, supply the nofollow value to whodb:
  58. -- nmap target --script whois --script-args whodb=nofollow
  59. -- nmap target --script whois --script-args whois={whodb=nofollow+ripe}
  60. -- # Note that only one service (the first one supplied) will be used in
  61. -- # conjunction with nofollow.
  62. --
  63. -- # To ensure discovery of smaller assignments even if larger ones
  64. -- # exist in the cache, supply the nocache value to whodb:
  65. -- nmap target --script whois --script-args whodb=nocache
  66. -- nmap target --script whois --script-args whois={whodb=nocache}
  67. -- @output
  68. -- Host script results:
  69. -- |  whois: Record found at whois.arin.net
  70. -- |  netrange: 64.13.134.0 - 64.13.134.63
  71. -- |  netname: NET-64-13-143-0-26
  72. -- |  orgname: Titan Networks
  73. -- |  orgid: INSEC
  74. -- |_ country: US stateprov: CA
  75.  
  76. author      = "jah <jah at zadkiel.plus.com>"
  77. license     = "See Nmap License: http://nmap.org/book/man-legal.html"
  78. runlevel    = 1
  79. categories  = {"discovery", "external", "safe"}
  80.  
  81. local url    = require "url"
  82. local http   = require "http"
  83. local ipOps  = require "ipOps"
  84. local stdnse = require "stdnse"
  85.  
  86.  
  87.  
  88. -------------------------------------------------------------------------------------------------------------------------
  89. --
  90. --
  91. --
  92. --
  93. -- This script will run only if the target IP address has been determined to be routable on the Internet.
  94.  
  95. hostrule = function( host )
  96.  
  97.     local is_private, err = ipOps.isPrivate( host.ip )
  98.     if err then
  99.       stdnse.print_debug( "%s Error in Hostrule: %s.", filename, err )
  100.       return false
  101.     end
  102.  
  103.     return not is_private
  104.  
  105. end
  106.  
  107.  
  108.  
  109. -------------------------------------------------------------------------------------------------------------------------
  110. --
  111. --
  112. --
  113. --
  114. -- Queries WHOIS services until an applicable record is found or the list of services to query
  115. -- is exhausted and finishes by displaying elements of an applicable record.
  116.  
  117. action = function( host )
  118.  
  119.   if not nmap.registry.whois then
  120.     ---
  121.     -- Data and flags shared between threads.
  122.     -- @name whois
  123.     -- @class table
  124.     --@field whoisdb_default_order          The default number and order of whois services to query.
  125.     --@field using_local_assignments_file   Set this to: false; to avoid using the data from IANA hosted assignments files (false when whodb=nofile).
  126.     --@field local_assignments_file_expiry  A period, between 0 and 7 days, during which cached assignments data may be used without being refreshed.
  127.     --@field init_done                      Set when <code>script_init</code> has been called and prevents it being called again.
  128.     --@field mutex                          A table of mutex functions, one for each service defined herein.  Allows a thread exclusive access to a
  129.     --                                service, preventing concurrent connections to it.
  130.     --@field nofollow                       A flag that prevents referrals to other whois records and allows the first record retrieved to be
  131.     --                                returned instead.  Set to true when whodb=nofollow
  132.     --@field using_cache                    A flag which modifies the size of ranges in a cache entry.  Set to false when whodb=nocache
  133.     --@field cache                          Storage for cached redirects, records and other data for output.
  134.     nmap.registry.whois = {}
  135.     nmap.registry.whois.whoisdb_default_order = {"arin","ripe","apnic"}
  136.     nmap.registry.whois.using_cache = true
  137.     nmap.registry.whois.using_local_assignments_file = true
  138.     nmap.registry.whois.local_assignments_file_expiry = "16h"
  139.     nmap.registry.whois.nofollow = false
  140.     nmap.registry.whois.cache = {}
  141.  
  142.   end
  143.  
  144.   -- script initialisation - threads must wait until this has been completed before continuing
  145.   local mutex = nmap.mutex( "whois" )
  146.   mutex "lock"
  147.   if not nmap.registry.whois.init_done then
  148.     script_init( host.ip )
  149.   end
  150.   mutex "done"
  151.  
  152.   ---
  153.   -- Holds field data captured from the responses of each service queried and includes additional information about the final desired record.
  154.   --
  155.   -- The table, indexed by whois service id, holds a table of fields captured from each queried service.  Once it has been determined that a record
  156.   -- represents the final record we wish to output, the existing values are destroyed and replaced with the one required record.  This is done purely
  157.   -- to make it easier to reference the data of a desired record.  Other values in the table are as follows.
  158.   -- @name data
  159.   -- @class table
  160.   --@field data.iana        is set after the table is initialised and is the number of times a response encountered represents "The Whole Address Space".
  161.   --                  If the value reaches 2 it is assumed that a valid record is held at ARIN.
  162.   --@field data.id          is set in <code>analyse_response</code> after final record and is the service name at which a valid record has been found.  Used in
  163.   --                  <code>format_data_for_output</code>.
  164.   --@field data.mirror      is set in <code>analyse_response</code> after final record and is the service name from which a mirrored record has been found.  Used in
  165.   --                  <code>format_data_for_output</code>.
  166.   --@field data.comparison  is set in <code>analyse_response</code> after final record and is a string concatenated from fields extracted from a record and which
  167.   --                  serves as a fingerprint for a record, used in <code>get_cache_key</code>, to compare two records for equality.
  168.   local data = {}
  169.   data.iana = 0
  170.  
  171.   ---
  172.   -- Used in the main loop to manage mutexes, the structure of tracking is as follows.
  173.   -- @name tracking
  174.   -- @class table
  175.   --@field this_db    The service for which a thread will wait for exclusive access before sending a query to it.
  176.   --@field next_db    The next service to query.  Allows a thread to continue in the main "while do" loop.
  177.   --@field last_db    The value of this_db after sending a query, used when exclusive access to a service is no longer required.
  178.   --@field completed  An array of services previously queried.
  179.   local tracking = {}
  180.   tracking.completed = {}
  181.  
  182.   tracking = get_next_action( tracking, host.ip )
  183.  
  184.   -- main loop
  185.   while tracking.next_db do
  186.  
  187.     local status, retval
  188.     tracking.this_db, tracking.next_db = tracking.next_db, nil
  189.  
  190.     nmap.registry.whois.mutex[tracking.this_db] "lock"
  191.  
  192.     status, retval = pcall( get_next_action, tracking, host.ip )
  193.     if not status then
  194.     stdnse.print_debug( "%s %s pcall caught an exception in get_next_action: %s.", filename, host.ip, retval )
  195.     else tracking = retval end
  196.  
  197.     if tracking.this_db then
  198.       -- do query
  199.       response = do_query( tracking.this_db, host.ip )
  200.       tracking.completed[#tracking.completed+1] = tracking.this_db
  201.  
  202.       -- analyse data
  203.       status, retval = pcall( analyse_response, tracking, host.ip, response, data )
  204.       if not status then
  205.       stdnse.print_debug( "%s %s pcall caught an exception in analyse_response: %s.", filename, host.ip, retval )
  206.       else data = retval end
  207.  
  208.       -- get next action
  209.       status, retval = pcall( get_next_action, tracking, host.ip )
  210.       if not status then
  211.         stdnse.print_debug( "%s %s pcall caught an exception in get_next_action: %s.", filename, host.ip, retval )
  212.         if not tracking.last_db then tracking.last_db, tracking.this_db = tracking.this_db or tracking.next_db, nil end
  213.       else tracking = retval end
  214.     end
  215.  
  216.     nmap.registry.whois.mutex[tracking.last_db] "done"
  217.     tracking.last_db = nil
  218.  
  219.   end
  220.  
  221.  
  222.   return output( host.ip, tracking.completed )
  223.  
  224. end -- action
  225.  
  226.  
  227.  
  228.  
  229. ----------------------------------------------------------------------------------------------------------------------------
  230. --
  231. --
  232. --
  233. --
  234. -- Determines whether or not to query a whois service and which one to query.  Checks the cache first - where there may be a redirect or a
  235. -- cached record.  If not, it trys to get a service from the assignments files if this was not previously attempted.  Finally, if a service has
  236. -- not yet been obtained the first unqueried service from whoisdb_default_order is used.  The tracking table is manipulated such that a thread
  237. -- knows its next move in the main loop.
  238. -- @param tracking  The Tracking table.
  239. -- @param ip        String representing the Target's IP address.
  240. -- @return          The supplied and possibly modified tracking table.
  241. -- @see             tracking, check_response_cache, get_db_from_assignments
  242.  
  243. function get_next_action( tracking, ip )
  244.  
  245.   if type( ip ) ~= "string" or ip == "" or type( tracking ) ~= "table" or type( tracking.completed ) ~= "table" then return nil end
  246.  
  247.   --next_db should always be nil when calling this
  248.   if tracking.next_db then return tracking end
  249.  
  250.  
  251.   -- check for cached redirects and records
  252.   local in_cache
  253.   in_cache, tracking.next_db = check_response_cache( ip )
  254.  
  255.   if in_cache and not tracking.next_db then
  256.  
  257.     -- found cached data - quit
  258.     tracking.this_db, tracking.last_db = nil, tracking.this_db
  259.     return tracking
  260.  
  261.   elseif in_cache and tracking.next_db then
  262.  
  263.     -- found cached redirect
  264.     if tracking.next_db ~= tracking.this_db then
  265.  
  266.       -- skip query to this_db and set last_db so we can unlock mutex
  267.       tracking.this_db, tracking.last_db = nil, tracking.this_db
  268.  
  269.     else
  270.  
  271.       -- we were already about to query this_db
  272.       tracking.next_db = nil
  273.  
  274.     end
  275.  
  276.     -- kill redirect if the user specified "nofollow"
  277.     if nmap.registry.whois.nofollow then tracking.next_db = nil end
  278.  
  279.     return tracking
  280.  
  281.   elseif not in_cache and tracking.this_db and table.concat( tracking.completed, " " ):match( tracking.this_db ) then
  282.  
  283.     -- we've already queried this_db so lets skip it and try whoisdb_default_order
  284.     tracking.last_db, tracking.this_db = tracking.this_db, nil
  285.  
  286.   end
  287.  
  288.  
  289.   -- try to find a service to query in the assignments files, if allowed
  290.   if nmap.registry.whois.using_local_assignments_file and not tracking.this_db and not tracking.last_db then
  291.  
  292.     tracking.next_db = get_db_from_assignments( ip )
  293.     if tracking.next_db and not table.concat( tracking.completed, " " ):match( tracking.next_db ) then
  294.       -- we got one we haven't queried - we probably haven't queried any yet.
  295.       return tracking
  296.     end
  297.  
  298.   end
  299.  
  300.  
  301.  -- get the next untried service from whoisdb_default_order
  302.  if not tracking.this_db and nmap.registry.whois.whoisdb_default_order then
  303.  
  304.     for i, db in ipairs( nmap.registry.whois.whoisdb_default_order ) do
  305.       if not table.concat( tracking.completed, " " ):match( db ) then
  306.         tracking.next_db = db
  307.         break
  308.       end
  309.     end
  310.  
  311.   end
  312.  
  313.   return tracking
  314.  
  315. end
  316.  
  317.  
  318.  
  319. ---
  320. -- Checks the registry for cached redirects and results applicable to the supplied Target's IP address.
  321. -- @param ip  String representing the Target's IP address.
  322. -- @return    Boolean True if the supplied IP address is within a range of addresses for which there is a cache entry and a redirect or a
  323. --            record is present; otherwise false.
  324. -- @return    ID of a service defined in whoisdb if a redirect is present; otherwise nil.
  325. -- @see       get_cache_key
  326.  
  327. function check_response_cache( ip )
  328.  
  329.   if not next( nmap.registry.whois.cache ) then return false, nil end
  330.   if type( ip ) ~= "string" or ip == "" then return false, nil end
  331.  
  332.   local ip_key = get_cache_key( ip )
  333.   if not ip_key then return false, nil end
  334.  
  335.   local cache_data = nmap.registry.whois.cache[ip_key]
  336.  
  337.   if cache_data.redirect then
  338.     -- redirect found in cache
  339.     return true, cache_data.redirect
  340.   elseif cache_data.data then
  341.     -- record found in cache
  342.     return true, nil
  343.   else
  344.     stdnse.print_debug( 1, "%s %s Error in check_response_cache: Empty Cache Entry was found.", filename, ip )
  345.   end
  346.  
  347.   return false, nil
  348.  
  349. end
  350.  
  351.  
  352.  
  353. ---
  354. -- Determines which entry in the cache is applicable to the Target and returns the key for that entry.
  355. -- @param ip  String representing the Target's IP address.
  356. -- @return    String key (IP address) of the cache entry applicable to the Target.
  357.  
  358. function get_cache_key( ip )
  359.  
  360.   -- if this ip cached an entry, then we'll use it except when it represents a found record and we're not using_cache
  361.   if nmap.registry.whois.cache[ip] and ( nmap.registry.whois.using_cache or nmap.registry.whois.cache[ip].redirect ) then
  362.     return ip
  363.   end
  364.  
  365.   -- When not using_cache, we compare our record to any others in the cache to avoid printing out the same record repeatedly.
  366.   local self_compare
  367.   if nmap.registry.whois.cache[ip] and nmap.registry.whois.cache[ip].data then
  368.     -- we should have a string which we can use to compare with other records
  369.     self_compare = nmap.registry.whois.cache[ip].data.comparison
  370.   end
  371.  
  372.   local cache_entries = {}
  373.   for ip_key, cache_data in pairs( nmap.registry.whois.cache ) do
  374.  
  375.     if type( ip_key ) == "string" and ip_key ~= "" and type( cache_data ) == "table" then
  376.  
  377.       -- compare and return original pointer
  378.       if self_compare and ip ~= ip_key and not cache_data.pointer and self_compare == cache_data.data.comparison then
  379.         nmap.registry.whois.cache[ip].pointer = ip_key
  380.         return ip_key
  381.       end
  382.  
  383.       -- check if ip is in a cached range and add the entry to cache_entries if it is
  384.       local in_range, err = ipOps.ip_in_range( ip, cache_data.range )
  385.       if in_range then
  386.         local t = {}
  387.         t.key = ip_key
  388.         t.range = cache_data.range
  389.         t.pointer = cache_data.pointer
  390.         cache_entries[#cache_entries+1] = t
  391.       end
  392.  
  393.     end
  394.  
  395.   end
  396.  
  397.   if #cache_entries == 0 then
  398.     -- no applicable cache entries
  399.     return nil
  400.   elseif #cache_entries == 1 then
  401.     -- just one applicable entry
  402.     return cache_entries[1].pointer or cache_entries[1].key
  403.   end
  404.  
  405.   -- more than one entry need sorting into ascending order
  406.   table.sort( cache_entries, smallest_range )
  407.  
  408.   -- we'll choose the smallest range
  409.   return cache_entries[1].key
  410.  
  411. end
  412.  
  413.  
  414.  
  415. ---
  416. -- Calculates the prefix length for the given assignment.
  417. -- @param range  String representing an IP address assignment
  418. -- @return       Number - prefix length of the assignment
  419.  
  420. function get_prefix_length( range )
  421.  
  422.   if type( range ) ~= "string" or range == "" then return nil end
  423.  
  424.   local first, last, err = ipOps.get_ips_from_range( range )
  425.   if err then return nil end
  426.  
  427.   first = ipOps.ip_to_bin( first ):reverse()
  428.   last = ipOps.ip_to_bin( last ):reverse()
  429.  
  430.   local hostbits = 0
  431.   for pos = 1, string.len( first ), 1 do
  432.  
  433.     if first:sub( pos, pos ) == "0" and last:sub( pos, pos ) == "1" then
  434.       hostbits = hostbits + 1
  435.     else
  436.       break
  437.     end
  438.  
  439.   end
  440.  
  441.   return ( string.len( first ) - hostbits )
  442.  
  443. end
  444.  
  445.  
  446.  
  447.  
  448. ---
  449. -- Performs a lookup against assignments data to determine which service to query for the supplied Target.
  450. -- @param ip  String representing the Target's IP address.
  451. -- @return    String id of the whois service to query, or nil.
  452.  
  453. function get_db_from_assignments( ip )
  454.  
  455.   if type( ip ) ~= "string" or ip == "" then return nil end
  456.  
  457.   local af
  458.   if ip:match( ":" ) then
  459.     af = "ipv6"
  460.   else
  461.     af = "ipv4"
  462.   end
  463.  
  464.   if not nmap.registry.whois.local_assignments_data or not nmap.registry.whois.local_assignments_data[af] then
  465.     stdnse.print_debug( 1, "%s Error in get_db_from_assignments: Missing assignments data in registry.", filename )
  466.     return nil
  467.   end
  468.  
  469.   if next( nmap.registry.whois.local_assignments_data[af] ) then
  470.     for _, assignment in ipairs( nmap.registry.whois.local_assignments_data[af] ) do
  471.       if ipOps.ip_in_range( ip, assignment.range.first .. "-" .. assignment.range.last ) then
  472.         return assignment.service
  473.       end
  474.     end
  475.   end
  476.  
  477.   return nil
  478.  
  479. end
  480.  
  481.  
  482.  
  483. ---
  484. -- Connects to a whois service (usually TCP port 43) and sends an IP address query, returning any response.
  485. -- @param db  String id of a service defined in whoisdb.
  486. -- @param ip  String representing the Target's IP address.
  487. -- @return    String response to query or nil.
  488.  
  489. function do_query(db, ip)
  490.  
  491.   if type( db ) ~= "string" or not nmap.registry.whois.whoisdb[db] then
  492.     stdnse.print_debug("%s %s Error in do_query: %s is not a defined Whois service.", filename, ip, db)
  493.     return nil
  494.   end
  495.  
  496.   local service = nmap.registry.whois.whoisdb[db]
  497.  
  498.   if type( service.hostname ) ~= "string" or service.hostname == "" then
  499.     stdnse.print_debug("%s %s Error in do_query: Invalid hostname for %s.", filename, ip, db)
  500.     return nil
  501.   end
  502.  
  503.   local query_data = ""
  504.   if type( service.preflag ) == "string" and service.preflag ~= "" then
  505.     query_data = service.preflag .. " "
  506.   end
  507.   query_data = query_data .. ip
  508.   if type( service.postflag ) == "string" and service.postflag ~= "" then
  509.     query_data = query_data .. service.postflag
  510.   end
  511.   query_data = query_data .. "\n"
  512.  
  513.   local socket = nmap.new_socket()
  514.   local catch = function()
  515.     stdnse.print_debug( "%s %s Connection to %s failed or was aborted! No Output for this Target.", filename, ip, db )
  516.     nmap.registry.whois.mutex[db] "done"
  517.     socket:close()
  518.   end
  519.  
  520.   local result, status, line = {}
  521.   local try = nmap.new_try( catch )
  522.  
  523.   socket:set_timeout( 10000 )
  524.   try( socket:connect( service.hostname, 43 ) )
  525.   try( socket:send( query_data ) )
  526.  
  527.   while true do
  528.     local status, lines = socket:receive_lines(1)
  529.     if not status then
  530.       break
  531.     else
  532.       result[#result+1] = lines
  533.     end
  534.   end
  535.  
  536.   socket:close()
  537.  
  538.   stdnse.print_debug(3, "%s %s Ended Query at %s.", filename, ip, db)
  539.  
  540.   if #result == 0 then
  541.     return nil
  542.   end
  543.  
  544.   return table.concat( result )
  545.  
  546. end
  547.  
  548.  
  549.  
  550. ---
  551. -- Extracts fields (if present) from the information returned in response to our query and determines whether it represents a referral to a
  552. -- record hosted elsewhere.  The referral is cached in the registry to allow threads for targets in the same assignment to avoid performing
  553. -- their queries to this service.  If it is not a referral, we assume it is the desired record and the extracted fields are cached in the
  554. -- registry ready for output.
  555. -- @param tracking  Tracking table.
  556. -- @param ip        String representing a Target's IP address.
  557. -- @param response  String obtained from a service in response to our query.
  558. -- @param data      Table of fields captured from previously queried services, indexed by service name.
  559. -- @return          The data table passed as a parameter which may have been added to or may contain only the fields extracted from the desired
  560. --                  record (in which case it will no longer be indexed by service name).
  561. -- @see             extract_objects_from_response, redirection_rules, constrain_response, add_to_cache
  562.  
  563. function analyse_response( tracking, ip, response, data )
  564.  
  565.   if type( response ) ~= "string" or response == "" then return data end
  566.  
  567.   local meta, mirrored_db
  568.   local last_db, this_db, next_db = tracking.last_db, (tracking.this_db or tracking.last_db), tracking.next_db
  569.   data[this_db] = {}
  570.  
  571.   -- check for foreign resource
  572.   for _, db in pairs( nmap.registry.whois.whoisdb ) do
  573.     if type( db ) == "table" and type( db.id ) == "string" and db.id ~= "iana" and db.id ~= this_db and type( db.hostname ) == "string" then
  574.       pattern = db.id:upper() .. ".*%s*resource:%s*" .. db.hostname
  575.       if response:match( pattern ) then
  576.         mirrored_db = db.id
  577.         meta = db
  578.         meta.redirects = nil
  579.         break
  580.       end
  581.     end
  582.   end
  583.  
  584.   meta = meta or nmap.registry.whois.whoisdb[this_db]
  585.  
  586.   -- do we recognize objects in the response?.
  587.   if type( meta ) == "table" and type( meta.fieldreq ) == "table" and type( meta.fieldreq.ob_exist ) == "string" then
  588.     have_objects = response:match( meta.fieldreq.ob_exist )
  589.   else
  590.     stdnse.print_debug( 2, "%s %s Could not check for objects, problem with meta data.", filename, ip )
  591.     have_objects = false
  592.   end
  593.  
  594.   -- if we do not recognize objects check for an known error/non-object message
  595.   if not have_objects then
  596.     stdnse.print_debug( 4, "%s %s %s has not responded with the expected objects.", filename, ip, this_db )
  597.     local tmp, msg
  598.     -- may have found our record saying something similar to "No Record Found"
  599.     for _, pattern in ipairs( nmap.registry.whois.m_none ) do
  600.       pattern_l = pattern:gsub( "$addr", ip:lower() )
  601.       pattern_u = pattern:gsub( "$addr", ip:upper() )
  602.       msg = response:match( pattern_l ) or response:match( pattern_u )
  603.       if msg then
  604.         stdnse.print_debug( 4, "%s %s %s responded with a message which is assumed to be authoritative (but may not be).", filename, ip, this_db )
  605.         break
  606.       end
  607.     end
  608.     -- may have an error
  609.     if not msg then
  610.       for _, pattern in ipairs( nmap.registry.whois.m_err ) do
  611.         msg = response:match( pattern )
  612.         if msg then
  613.           stdnse.print_debug( 4, "%s %s %s responded with an ERROR message.", filename, ip, this_db )
  614.           break
  615.         end
  616.       end
  617.     end
  618.     -- if we've recognized a non-object message,
  619.     if msg then
  620.       add_to_cache( ip, nil, nil, "Message from " .. nmap.registry.whois.whoisdb[this_db].hostname .. "\n" .. msg )
  621.       return data
  622.     end
  623.   end
  624.  
  625.   -- the query response may not contain the set of objects we were expecting and we do not recognize the response message.
  626.   -- it may contain a record mirrored (or found by recursion) from a different service
  627.   if not have_objects then
  628.     local foreign_obj
  629.     for setname, set in pairs( nmap.registry.whois.fields_meta ) do
  630.       if set ~= nmap.registry.whois.whoisdb[this_db].fieldreq and response:match(set.ob_exist) then
  631.         foreign_obj = setname
  632.         stdnse.print_debug( 4, "%s %s %s seems to have responded using the set of objects named: %s.", filename, ip, this_db, foreign_obj )
  633.         break
  634.       end
  635.     end
  636.     if foreign_obj and foreign_obj == "rpsl" then
  637.       mirrored_db = nmap.registry.whois.whoisdb.ripe.id
  638.       meta = nmap.registry.whois.whoisdb.ripe
  639.       meta.redirects = nil
  640.       have_objects = true
  641.       stdnse.print_debug( 4, "%s %s %s will use the display properties of ripe.", filename, ip, this_db )
  642.     elseif foreign_obj then
  643.       -- find a display to match the objects.
  644.       for some_db, db_props in pairs( nmap.registry.whois.whoisdb ) do
  645.         if db_props.fieldreq and nmap.registry.whois.fields_meta[foreign_obj] and db_props.fieldreq == nmap.registry.whois.fields_meta[foreign_obj] then
  646.           mirrored_db = nmap.registry.whois.whoisdb[some_db].id
  647.           meta = nmap.registry.whois.whoisdb[some_db]
  648.           meta.redirects = nil
  649.           have_objects = true
  650.           stdnse.print_debug( 4, "%s %s %s will use the display properties of %s.", filename, ip, this_db, some_db )
  651.           break
  652.         end
  653.       end
  654.     end -- if foreign_obj
  655.   end
  656.  
  657.   -- extract fields from the entire response for record/redirect discovery
  658.   if have_objects then
  659.     stdnse.print_debug( 4, "%s %s Parsing Query response from %s.", filename, ip, this_db )
  660.     data[this_db] = extract_objects_from_response( response, this_db, ip, meta )
  661.   end
  662.  
  663.   local response_chunk, found, nextdb
  664.  
  665.   -- do record/redirect discovery, cache found redirect
  666.   if not nmap.registry.whois.nofollow and have_objects and meta.redirects then
  667.     stdnse.print_debug( 4, "%s %s Testing response for redirection.", filename, ip )
  668.     found, nextdb, data.iana = redirection_rules( this_db, ip, data, meta )
  669.   end
  670.  
  671.   -- get most specific assignment and handle arin's organisation-focused record layout and then
  672.   -- modify the data table depending on whether we're redirecting or quitting
  673.   if have_objects then
  674.  
  675.     stdnse.print_debug( 5, "%s %s Extracting Fields from response.", filename, ip )
  676.  
  677.     -- optionally constrain response to a more focused area
  678.     -- discarding previous extraction
  679.     if meta.smallnet_rule then
  680.       local offset, ptr, strbgn, strend
  681.       response_chunk, offset = constrain_response( response, this_db, ip, meta )
  682.       if offset > 0 then
  683.         data[this_db] = extract_objects_from_response( response_chunk, this_db, ip, meta )
  684.       end
  685.       if offset > 1 and meta.unordered then
  686.         -- fetch an object immediately in front of inetnum
  687.         stdnse.print_debug( 5, "%s %s %s Searching for an object group immediately before this range.", filename, ip, this_db )
  688.         -- split objects from the record, up to offset.  Last object should be the one we want.
  689.         local obj_sel = stdnse.strsplit( "\r?\n\r?\n", response:sub( 1, offset ) )
  690.         response_chunk = "\n" .. obj_sel[#obj_sel] .. "\n"
  691.         -- check if any of the objects we like match this single object in response chunk
  692.         for ob, t in pairs( meta.fieldreq ) do
  693.           if ob ~= "ob_exist" and type( t.ob_start ) == "string" and response_chunk:match( t.ob_start ) then
  694.              data[this_db][ob] = extract_objects_from_response( response_chunk, this_db, ip, meta, ob )
  695.           end
  696.         end
  697.  
  698.       end -- if offset
  699.     end -- if meta.smallnet_rule
  700.  
  701.     -- collect, from each extracted object, the tables of field values and positions and concatenate these
  702.     -- to provide the ability to easily compare two results
  703.     local coll, comp = {}, ""
  704.     for ob, t in pairs( data[this_db] ) do
  705.       for i, comp_string in pairs( t.for_compare ) do
  706.         coll[#coll+1] = { i, comp_string }
  707.       end
  708.       -- kill these now they're collected
  709.       data[this_db][ob].for_compare = nil
  710.     end
  711.     -- sort them by position in the record, ascending
  712.     table.sort( coll, function(a,b) return a[1]<b[1] end )
  713.     -- concatenate them to create a long string we can compare.  Assign to .comparison after the debug bit following...
  714.     for i, v in ipairs( coll ) do
  715.       comp = comp .. v[2]
  716.     end
  717.  
  718.     -- DEBUG
  719.     stdnse.print_debug( 6, "%s %s %s Fields captured :", filename, ip, this_db )
  720.     for ob, t in pairs( data[this_db] ) do
  721.       for fieldname, fieldvalue in pairs( t ) do
  722.         stdnse.print_debug( 6, "%s %s %s %s.%s %s.", filename, ip, this_db, ob, fieldname, fieldvalue )
  723.       end
  724.     end
  725.  
  726.     -- add comparison string to extracted data
  727.     data[this_db].comparison = comp
  728.  
  729.     -- add mirrored_db to extracted data
  730.     data[this_db].mirror = mirrored_db
  731.  
  732.     -- If we are accepting a record, only cache the data for that record
  733.     if not nextdb or nmap.registry.whois.nofollow then
  734.       -- no redirect - accept as result and clear any previous data
  735.       data = data[this_db]
  736.       data.id = this_db
  737.     elseif nextdb and table.concat( tracking.completed, " " ):match( nextdb ) then
  738.       -- redirected to a previously queried service - accept as result
  739.       data = data[nextdb]
  740.       data.id = nextdb
  741.       nextdb = nil
  742.     elseif have_objects and ( data.iana > 1 ) and not table.concat( tracking.completed, " " ):match( nmap.registry.whois.whoisdb.arin.id ) then
  743.       -- two redirects to IANA - query ARIN next (which we should probably have done already!)
  744.       nextdb = nmap.registry.whois.whoisdb.arin.id
  745.     elseif have_objects and ( data.iana > 1 ) and table.concat( tracking.completed, " " ):match( nmap.registry.whois.whoisdb.arin.id ) then
  746.       -- two redirects to IANA - accept result from ARIN
  747.       data = data[nmap.registry.whois.whoisdb.arin.id]
  748.       data.id = nmap.registry.whois.whoisdb.arin.id
  749.       nextdb = nil
  750.     end
  751.  
  752.     -- cache our analysis
  753.     local range
  754.     if data[this_db] and data[this_db].ob_netnum then
  755.       range = data[this_db].ob_netnum[meta.reg]
  756.     elseif data.ob_netnum and data.mirror then
  757.       range = data.ob_netnum[nmap.registry.whois.whoisdb[data.mirror].reg]
  758.     elseif data.ob_netnum then
  759.       range = data.ob_netnum[nmap.registry.whois.whoisdb[data.id].reg]
  760.     end
  761.  
  762.     -- if nocache then enforce a smallest allowed prefix length
  763.     -- (these values should match those in add_to_cache)
  764.     if not nmap.registry.whois.using_cache and not nextdb then
  765.       local smallest_allowed_prefix = 29
  766.       if range:match( ":" ) then
  767.         smallest_allowed_prefix = 48
  768.       end
  769.       local range_prefix = get_prefix_length( range )
  770.       if type( range_prefix ) ~= "number" or range_prefix < smallest_allowed_prefix then
  771.         range = nil
  772.       end
  773.     end
  774.  
  775.     -- prevent caching (0/0 or /8) or (::/0 or /23) or
  776.     range = not_short_prefix( ip, range, nextdb )
  777.  
  778.     -- add to cache
  779.     add_to_cache( ip, range, nextdb, data )
  780.  
  781.   end -- if have_objects
  782.  
  783.   return data
  784.  
  785. end
  786.  
  787.  
  788.  
  789. ---
  790. -- Extracts Whois record objects (or a single object) and accompanying fields from the supplied (possibly partial) response to a whois query.
  791. -- If a fifth parameter specific_object is not supplied, all objects defined in fields_meta will be captured if they are present in the response.
  792. -- @param response_string  String obtained from a service in response to our query.
  793. -- @param db               String id of the whois service queried.
  794. -- @param ip               String representing the Target's IP address.
  795. -- @param meta             Table, nmap.registry.whois.whoisdb[db] where db is either the service queried or a mirrored service.
  796. -- @param specific_object  Optional string index of a single object defined in fields_meta (e.g. "inetnum").
  797. -- @return                 Table indexed by object name containing the fields captured for each object found.
  798.  
  799. function extract_objects_from_response( response_string, db, ip, meta, specific_object )
  800.  
  801.   local objects_to_extract = {}
  802.   local extracted_objects = {}
  803.  
  804.   if type( response_string ) ~= "string" or response_string == "" then return {} end
  805.   if type( meta ) ~= "table" or type( meta.fieldreq ) ~= "table" then return {} end
  806.  
  807.   -- we either receive a table for one object or for all objects
  808.   if type( specific_object ) == "string" and meta.fieldreq[specific_object] then
  809.     objects_to_extract[specific_object] = meta.fieldreq[specific_object]
  810.     stdnse.print_debug( 5, "%s %s Extracting a single object: %s.", filename, ip, specific_object )
  811.   else
  812.     stdnse.print_debug( 5, "%s %s Extracting all objects.", filename, ip )
  813.     objects_to_extract = meta.fieldreq
  814.   end
  815.  
  816.   for object_name, object in pairs( objects_to_extract ) do
  817.     if object_name and object_name ~= "ob_exist" then
  818.       stdnse.print_debug(5, "%s %s Seeking object group: %s.", filename, ip, object_name)
  819.       extracted_objects[object_name] = {}
  820.       extracted_objects[object_name].for_compare = {} -- this will allow us to compare two tables
  821.       -- get a substr of response_string that corresponds to a single object
  822.       local ob_start, j = response_string:find( object.ob_start )
  823.       local i, ob_end = response_string:find( object.ob_end, j )
  824.       -- if we could not find the end, make the end EOF
  825.       ob_end = ob_end or -1
  826.       if ob_start and ob_end then
  827.         stdnse.print_debug(5, "%s %s Capturing: %s with indices %s and %s.", filename, ip, object_name, ob_start, ob_end )
  828.         local obj_string = response_string:sub( ob_start, ob_end )
  829.         for fieldname, pattern in pairs( object ) do
  830.           if fieldname ~= "ob_start" and fieldname ~= "ob_end" then
  831.             local data_pos, data_string = obj_string:find( pattern ), trim( obj_string:match( pattern ) )
  832.             if data_string then
  833.               extracted_objects[object_name][fieldname] = data_string
  834.               extracted_objects[object_name].for_compare[data_pos+ob_start] = data_string
  835.             end
  836.           end
  837.         end
  838.        end -- if ob_start and ob_end
  839.  
  840.     end -- if object_name
  841.   end -- for object_name
  842.  
  843.   if specific_object then extracted_objects = extracted_objects[specific_object] end -- returning one object
  844.  
  845.   return extracted_objects
  846.  
  847. end -- function
  848.  
  849.  
  850.  
  851. ---
  852. -- Checks for referrals in fields extracted from the whois query response.
  853. -- @param db    String id of the whois service queried.
  854. -- @param ip    String representing the Target's IP address.
  855. -- @param data  Table, indexed by whois service id, of extracted fields.
  856. -- @param meta  Table, nmap.registry.whois.whoisdb[db] where db is either the service queried or a mirrored service.
  857. -- @return      Boolean "found". True if a referral is not found (i.e. No Referral means the desired record has been "found"), otherwise False.
  858. -- @return      String "redirect". Service id to which we are referred, or nil.
  859. -- @return      Number "iana_count". This is the total number of referral to IANA for this Target (for all queries) and is stored in data.iana.
  860. -- @see         redirection_validation
  861.  
  862. function redirection_rules( db, ip, data, meta )
  863.  
  864.   if type( db ) ~= "string" or db == "" or type( ip ) ~= "string" or ip == "" or type( data ) ~= "table" or not next( data ) then
  865.     return false, nil, nil
  866.   end
  867.  
  868.   local found = false
  869.   local redirect = nil
  870.   local iana_count
  871.   if type( data.iana ) == "number" then
  872.     iana_count = data.iana
  873.   else
  874.     iana_count = 0
  875.   end
  876.  
  877.   if not meta or not meta.redirects then
  878.     return found, redirect, iana_count
  879.   end
  880.  
  881.   ---
  882.   -- Decides the value of a redirect and whether it should be followed.  Referrals to IANA, found in whois records that represent the
  883.   -- "Whole Address Space",  are acted upon by redirecting to ARIN or accepting the record from ARIN if it was previously queried.  This
  884.   -- function also catches (ignores) referrals to the referring service - which happens as a side-effect of the method of redirection detection.
  885.   -- The return values of this function will be returned by its parent function.
  886.   -- @param directed_to    String id of a whois service.
  887.   -- @param directed_from  String id of a whois service.
  888.   -- @param icnt           Number of total redirects to IANA.
  889.   -- @return               Boolean "found". True if a redirect is not found or ignored, otherwise False.
  890.   -- @return               String "redirect". Service id to which we are redirected, or nil.
  891.   -- @return               Number "iana_count" which is incremented here if applicable.
  892.  
  893.   local redirection_validation = function( directed_to, directed_from, icnt )
  894.  
  895.     local iana = nmap.registry.whois.whoisdb.iana.id
  896.     local arin = nmap.registry.whois.whoisdb.arin.id
  897.  
  898.     -- arin record points to iana so we won't follow and we assume we have our record
  899.     if directed_to == iana and directed_from == arin then
  900.       stdnse.print_debug( 4, "%s %s %s Accept arin record (matched IANA).", filename, ip, directed_from )
  901.       return true, nil, ( icnt+1 )
  902.     end
  903.  
  904.     -- non-arin record points to iana so we query arin next
  905.     if directed_to == iana then
  906.       stdnse.print_debug( 4, "%s %s Redirecting to arin (matched IANA).", filename, ip )
  907.       return false, arin, ( icnt+1 )
  908.     end
  909.  
  910.     -- a redirect, but not to iana or to self, so we follow it.
  911.     if directed_to ~= nmap.registry.whois.whoisdb[directed_from].id then
  912.       stdnse.print_debug( 4, "%s %s %s redirects us to %s.", filename, ip, directed_from, directed_to )
  913.       return false, directed_to, icnt
  914.     end
  915.  
  916.     -- redirect to self
  917.     return true, nil, icnt
  918.  
  919.   end --redirection_validation
  920.  
  921.     -- iterate over each table of redirect info for a specific field
  922.   for _, redirect_elems in ipairs( meta.redirects ) do
  923.  
  924.     local obj, fld, pattern = unpack( redirect_elems )   -- three redirect elements
  925.     -- if a field has been captured for the given redirect info
  926.     if data[db][obj] and data[db][obj][fld] then
  927.  
  928.       stdnse.print_debug( 5, "%s %s Seek redirect in object: %s.%s for %s.", filename, ip, obj, fld, pattern )
  929.       -- iterate over nmap.registry.whois.whoisdb to find pattern (from each service) in the designated field
  930.       for member, mem_properties in pairs( nmap.registry.whois.whoisdb ) do
  931.  
  932.         -- if pattern if found in the field, we have a redirect to member
  933.         if type( mem_properties[pattern] ) == "string" and string.lower( data[db][obj][fld] ):match( mem_properties[pattern] ) then
  934.  
  935.           stdnse.print_debug( 5, "%s %s Matched %s in %s.%s.", filename, ip, pattern, obj, fld )
  936.           return redirection_validation( nmap.registry.whois.whoisdb[member].id, db, iana_count )
  937.  
  938.         elseif type( mem_properties[pattern] ) == "table" then
  939.  
  940.           -- pattern is an array of patterns
  941.           for _, pattn in ipairs( mem_properties[pattern] ) do
  942.             if type( pattn ) == "string" and string.lower( data[db][obj][fld] ):match( pattn ) then
  943.               stdnse.print_debug( 5, "%s %s Matched %s in %s.%s.", filename, ip, pattern, obj, fld )
  944.               return redirection_validation( nmap.registry.whois.whoisdb[member].id, db, iana_count )
  945.             end
  946.           end
  947.  
  948.         end
  949.  
  950.       end -- for mem, mem_properties
  951.  
  952.     end
  953.  
  954.   end -- for _,v in ipairs
  955.  
  956.   -- if redirects have not been found then assume that the record has been found.
  957.   found = true
  958.   return found, redirect, iana_count
  959.  
  960. end
  961.  
  962.  
  963.  
  964. ---
  965. -- Attempts to reduce the query response to a subset containing the most specific assignment information.
  966. -- It does this by collecting inetnum objects (and their positions in the response) and choosing the smallest assignment represented by them.
  967. -- A subset beginning with the most specific inetnum object and ending before any further inetnum objects is returned along with the position
  968. -- of the subset within the entire response.
  969. -- @param response  String obtained from a whois service in response to our query.
  970. -- @param db        String id of the service from which the response was obtained.
  971. -- @param ip        String representing the Target's IP address.
  972. -- @param meta      Table, nmap.registry.whois.whoisdb[db] where db is either the service queried or a mirrored service.
  973. -- @return          String containing the most specific part of the response (or the entire response if only one inetneum object is present).
  974. -- @return          Number position of the start of the most specific part of the response.
  975. -- @see             smallest_range
  976.  
  977. function constrain_response( response, db, ip, meta )
  978.   local strbgn = 1
  979.   local strend = 1
  980.   local ptr = 1
  981.   local mptr = {}
  982.   local bound = nil
  983.  
  984.   -- collect all inetnums objects (and their position) into a table
  985.   while strbgn and meta.fieldreq do
  986.     strbgn, strend = response:find( meta.fieldreq.ob_exist, strend )
  987.     if strbgn then
  988.       local pair = {}
  989.       pair.pointer = strbgn
  990.       pair.range = trim( response:match( meta.smallnet_rule, strbgn ) )
  991.       mptr[#mptr+1] = pair
  992.     end
  993.   end
  994.  
  995.   if # mptr > 1 then
  996.     -- find the closest one to host.ip and constrain the response to it
  997.     stdnse.print_debug( 5, "%s %s %s Focusing on the smallest of %s address ranges.", filename, ip, db, #mptr )
  998.     -- sort the table mptr into nets ascending
  999.     table.sort( mptr, smallest_range )
  1000.     -- select the first net that includes host.ip
  1001.     local str_net
  1002.     local index
  1003.     for i, pointer_to_inetnum in ipairs( mptr ) do
  1004.       if ipOps.ip_in_range( ip, pointer_to_inetnum.range ) then
  1005.         str_net = pointer_to_inetnum.range
  1006.         ptr = pointer_to_inetnum.pointer
  1007.         index = i
  1008.         break
  1009.       end
  1010.     end
  1011.  
  1012.     if mptr[index+1] and ( mptr[index+1].pointer > mptr[index].pointer ) then
  1013.       bound = mptr[index+1].pointer
  1014.     end
  1015.     stdnse.print_debug(5, "%s %s %s Smallest range containing target IP addr. is %s.", filename, ip, db, trim( str_net ) )
  1016.     local dbg = "%s %s %s smallest range is offset from %s to %s."
  1017.     -- isolate inetnum and associated objects
  1018.     if bound then
  1019.       stdnse.print_debug(5, dbg, filename, ip, db, ptr, bound)
  1020.       -- get from pointer to bound
  1021.       return response:sub(ptr,bound), ptr
  1022.     else
  1023.       stdnse.print_debug(5, dbg, filename, ip, db, ptr, "the end")
  1024.       -- or get the whole thing from the pointer onwards
  1025.       return response:sub(ptr), ptr
  1026.     end
  1027.   end -- if # mptr
  1028.  
  1029.   return response, 0
  1030.  
  1031. end -- function
  1032.  
  1033.  
  1034.  
  1035. ---
  1036. -- This function prevents the caching of large ranges in certain circumstances which would adversely affect lookups against the cache.
  1037. -- Specifically we don't allow a cache entry including either a referral or a found record with a range equal to 0/0 or ::/0.
  1038. -- Instead we cache an /8 or, in the case of IPv6, /23 - These are large, but safer ranges.
  1039. -- Additionally, we don't allow a cache entry for a found record with ranges larger than IPv4 /8 and IPv6 /23.
  1040. -- Instead we cache an /24 or, in the case of IPv6, /96 - These are small ranges and are a fair trade-off between accuracy and repeated queries.
  1041. -- @param ip     String representing the Target's IP address.
  1042. -- @param range  String representing a range of IP addresses.
  1043. -- @usage        range = not_short_prefix( ip, range )
  1044. -- @return       String range - either the supplied, or a modified one (or nil in case of an error).
  1045. -- @see          get_assignment
  1046.  
  1047. function not_short_prefix( ip, range, redirect )
  1048.  
  1049.   if type( range ) ~= "string" or range == "" then return nil end
  1050.  
  1051.   local err, zp_first, zp_last, fake_prefix, first, last = {}
  1052.   if range:match( ":" ) then
  1053.     short_prefix = 23
  1054.     safe_prefix = 96
  1055.     zero_first, zero_last, err[#err+1] = ipOps.get_ips_from_range( "::/0" )
  1056.   else
  1057.     short_prefix = 8
  1058.     safe_prefix = 24
  1059.     zero_first, zero_last, err[#err+1] = ipOps.get_ips_from_range( "0/0" )
  1060.   end
  1061.  
  1062.   first, last, err[#err+1] = ipOps.get_ips_from_range( range )
  1063.  
  1064.   if #err > 0 then
  1065.     stdnse.print_debug( 1, "%s Error in not_short_prefix: s%.", filename, table.concat( err, " " ) )
  1066.     return nil
  1067.   end
  1068.  
  1069.   if ipOps.compare_ip( first, "eq", zero_first ) and ipOps.compare_ip( last, "eq", zero_last ) then
  1070.     return ( get_assignment ( ip, short_prefix ) )
  1071.   elseif not redirect and ( get_prefix_length( range ) <= short_prefix ) then
  1072.     return ( get_assignment ( ip, safe_prefix ) )
  1073.   end
  1074.  
  1075.   return range
  1076.  
  1077. end
  1078.  
  1079.  
  1080.  
  1081. ---
  1082. -- Caches discovered records and referrals in the registry.
  1083. -- The cache is indexed by the Target IP addresses sent as Whois query terms.
  1084. -- A lookup against the cache is performed by testing the cached IP address range, hence a range must always be present in each cache entry.
  1085. -- Where a range is not passed as a parameter, a small assignment containing the Target's IP address is instead cached.
  1086. -- Either a referral or output data should also be present in the cache - so one or the other should always be passed as a parameter.
  1087. -- @param ip         String representing the Target's IP address.
  1088. -- @param range      String representing the most specific assignment found in a whois record.  May be nil.
  1089. -- @param redirect   String id of a referred service defined in whoisdb.
  1090. -- @param data       Table or String of extracted data.
  1091. -- @see              get_assignment
  1092.  
  1093. function add_to_cache( ip, range, redirect, data )
  1094.  
  1095.   if type( ip ) ~= "string" or ip == "" then return end
  1096.  
  1097.   local af, longest_prefix
  1098.   if ip:match( ":" ) then
  1099.     af = "ipv6"
  1100.     longest_prefix = 48 -- increased from 32 (20080902).
  1101.   else
  1102.     af = "ipv4"
  1103.     longest_prefix = 29 -- 8 hosts
  1104.   end
  1105.  
  1106.   -- we need to cache some range so we'll cache the small assignment that includes ip.
  1107.   if type( range ) ~= "string" or type( get_prefix_length( range ) ) ~= "number" then
  1108.     range = get_assignment( ip, longest_prefix )
  1109.     stdnse.print_debug(5, "%s %s Caching an assumed Range: %s", filename, ip, range)
  1110.   end
  1111.  
  1112.   nmap.registry.whois.cache[ip] = {} -- destroy any previous cache entry for this target.
  1113.   nmap.registry.whois.cache[ip].data = data
  1114.   nmap.registry.whois.cache[ip].range = range
  1115.   nmap.registry.whois.cache[ip].redirect = redirect
  1116.  
  1117. end
  1118.  
  1119.  
  1120.  
  1121. ---
  1122. -- When passed to <code>table.sort</code>, will sort a table of tables containing IP address ranges in ascending order of size.
  1123. -- Identical ranges will be sorted in descending order of their position within a record if it is present.
  1124. -- @param range_1  Table: {range = String, pointer = Number}
  1125. --                 where range is an IP address range and pointer is the position of that range in a record.
  1126. -- @param range_2  Same as range_1.
  1127. -- @return         Boolean True if the positions of range_1 and range_2 in the table being sorted are correct, otherwise false.
  1128.  
  1129. function smallest_range( range_1, range_2 )
  1130.  
  1131.   local sorted = true  -- return value (defaulting true to avoid a loop)
  1132.   local r1_first, r1_last = ipOps.get_ips_from_range( range_1.range )
  1133.   local r2_first, r2_last = ipOps.get_ips_from_range( range_2.range )
  1134.  
  1135.   if range_1.pointer and ipOps.compare_ip( r1_first, "eq", r2_first ) and ipOps.compare_ip( r1_last, "eq", r2_last )
  1136.   and range_1.pointer < range_2.pointer then
  1137.     sorted = false
  1138.   end
  1139.  
  1140.   if ipOps.compare_ip( r1_first, "le", r2_first ) and ipOps.compare_ip( r1_last, "ge", r2_last ) then sorted = false end
  1141.  
  1142.   return sorted
  1143.  
  1144. end
  1145.  
  1146.  
  1147.  
  1148. ---
  1149. -- Given an IP address and a prefix length, returns a string representing a valid IP address assignment (size is not checked) which contains
  1150. -- the supplied IP address.  For example, with ip = 192.168.1.187 and prefix = 24 the return value will be 192.168.1.1-192.168.1.255
  1151. -- @param ip      String representing an IP address.
  1152. -- @param prefix  String or number representing a prefix length.  Should be of the same address family as ip.
  1153. -- @return        String representing a range of addresses from the first to the last hosts (or nil in case of an error).
  1154. -- @return        Nil or error message in case of an error.
  1155.  
  1156. function get_assignment( ip, prefix )
  1157.  
  1158.   local some_ip, err = ipOps.ip_to_bin( ip )
  1159.   if err then return nil, err end
  1160.  
  1161.   prefix = tonumber( prefix )
  1162.   if not prefix or ( prefix < 0 ) or ( prefix > string.len( some_ip ) ) then
  1163.     return nil, "Error in get_assignment: Invalid prefix length."
  1164.   end
  1165.  
  1166.   local hostbits = string.sub( some_ip, prefix + 1 )
  1167.   hostbits = string.gsub( hostbits, "1", "0" )
  1168.   local first = string.sub( some_ip, 1, prefix ) .. hostbits
  1169.   err = {}
  1170.   first, err[#err+1] = ipOps.bin_to_ip( first )
  1171.   last, err[#err+1] = ipOps.get_last_ip( ip, prefix )
  1172.   if #err > 0 then return nil, table.concat( err, " " ) end
  1173.  
  1174.   return first .. "-" .. last
  1175.  
  1176. end
  1177.  
  1178.  
  1179.  
  1180. ---
  1181. -- Controls what to output at the end of the script execution.  Attempts to get data from the registry.  If the data is a string it is output as
  1182. -- it is.  If the data is a table then <code>format_data_for_output</code> is called.  If there is no cached data, nothing will be output.
  1183. -- @param ip                String representing the Target's IP address.
  1184. -- @param services_queried  Table of strings. Each is the id of a whois service queried for the Target (tracking.completed).
  1185. -- @return                  String - Host Script Results.
  1186. -- @see                     get_output_from_cache, format_data_for_output
  1187.  
  1188. function output( ip, services_queried )
  1189.  
  1190.   local data = get_output_from_cache( ip )
  1191.  
  1192.   if type( data ) == "string" then
  1193.     return data
  1194.   elseif type( data ) == "table" then
  1195.     return format_data_for_output( data )
  1196.   end
  1197.  
  1198.   if type( services_queried ) ~= "table" then
  1199.     stdnse.print_debug( "%s %s Error in output(): No data found.", filename, ip )
  1200.     return nil
  1201.   elseif #services_queried == 0 then
  1202.     stdnse.print_debug( "%s %s Error in output(): No data found, no queries were completed.", filename, ip )
  1203.     return nil
  1204.   elseif #services_queried > 0 then
  1205.     stdnse.print_debug( "%s %s Error in output(): No data found - could not understand query responses.", filename, ip )
  1206.     return nil
  1207.   end
  1208.  
  1209.   return nil -- just to be safe
  1210.  
  1211. end
  1212.  
  1213.  
  1214.  
  1215. ---
  1216. -- Retrieves data applicable to the Target from the registry.  Cached data is only returned if the Target IP matches a key in the cache.
  1217. -- If the Target IP is in a range for which there exists cached data then a pointer string is instead returned.
  1218. -- @param ip  String representing the Target's IP address.
  1219. -- @return    Table or string or nil.
  1220. -- @see       get_cache_key
  1221.  
  1222. function get_output_from_cache( ip )
  1223.  
  1224.   local ip_key = get_cache_key( ip )
  1225.   if not ip_key then
  1226.     stdnse.print_debug( 1, "%s %s Error in get_output_from_cache().", filename, ip )
  1227.     return nil
  1228.   end
  1229.  
  1230.   local cache_data = nmap.registry.whois.cache[ip_key]
  1231.  
  1232.   if ip == ip_key then
  1233.     return cache_data.data
  1234.   else
  1235.     return "See the result for " .. ip_key .. "."
  1236.   end
  1237.  
  1238. end
  1239.  
  1240.  
  1241.  
  1242. ---
  1243. -- Uses the output_short or output_long tables to format the supplied table of data for output as a string.
  1244. -- @param data  Table of captured fields grouped into whois record objects from a single record.
  1245. --              data.id is a string id of the service from which the record was retrieved and data.mirror is a string id of a mirrored service.
  1246. -- @return      String, ready for output (i.e. to be returned by action() ).
  1247.  
  1248. function format_data_for_output( data )
  1249.   -- DISPLAY THE FOUND RECORD
  1250.   -- ipairs over the table that dictates the order in which fields
  1251.   -- should be output
  1252.  
  1253.   local output, display_owner, display_rules = {}
  1254.   if data.mirror then
  1255.     display_owner = nmap.registry.whois.whoisdb[data.mirror]
  1256.   else
  1257.     display_owner = nmap.registry.whois.whoisdb[data.id]
  1258.   end
  1259.  
  1260.   if nmap.verbosity() > 0 then
  1261.     display_rules = display_owner.output_long or display_owner.output_short
  1262.   else
  1263.     display_rules = display_owner.output_short or display_owner.output_long
  1264.   end
  1265.   if not display_rules then return "Could not format results for display." end
  1266.  
  1267.   output[#output+1] = "Record found at "
  1268.   output[#output+1] = nmap.registry.whois.whoisdb[data.id].hostname
  1269.  
  1270.   for _, objects in ipairs( display_rules ) do
  1271.  
  1272.     local object_name, fields
  1273.     if type( objects[1] ) == "string" and objects[1] ~= "" and data[objects[1]] then
  1274.       object_name = objects[1]
  1275.     end
  1276.     if object_name and type( objects[2] ) == "table" and #objects[2] > 0 then
  1277.       fields = objects[2]
  1278.     end
  1279.  
  1280.     if fields then
  1281.       for _, field_name in ipairs( fields ) do
  1282.         if type( field_name ) == "string" and data[object_name][field_name] then
  1283.  
  1284.           output[#output+1] = " \n"
  1285.           output[#output+1] = field_name
  1286.           output[#output+1] = ": "
  1287.           output[#output+1] = data[object_name][field_name]
  1288.  
  1289.         elseif type( field_name ) == "table" then
  1290.  
  1291.           output[#output+1] = " \n"
  1292.  
  1293.           for _, field_name_sameline in ipairs( field_name ) do
  1294.             if type( field_name_sameline ) == "string" and data[object_name][field_name_sameline] then
  1295.  
  1296.               output[#output+1] = field_name_sameline
  1297.               output[#output+1] = ": "
  1298.               output[#output+1] = data[object_name][field_name_sameline]
  1299.               output[#output+1] = " "
  1300.  
  1301.             end
  1302.           end
  1303.  
  1304.         end
  1305.       end
  1306.     end
  1307.  
  1308.   end
  1309.  
  1310.   if #output < 3 then return "Could not display any information." end
  1311.  
  1312.   return ( table.concat( output ):gsub( "[%s\n]\n", "\n" ) )
  1313.  
  1314. end
  1315.  
  1316.  
  1317.  
  1318. ---
  1319. -- Trims space characters from either end of a string and converts an empty string to nil.
  1320. -- @param to_trim  String to be trimmed.
  1321. -- @return         String, trimmed.  If the string is empty before or after trimming (or if the parameter was not a string) then returns nil.
  1322.  
  1323. function trim( to_trim )
  1324.  
  1325.   if type( to_trim ) ~= "string" or to_trim == "" then return nil end
  1326.   local trimmed = ( string.gsub( to_trim, "^%s*(.-)%s*$", "%1" ) )
  1327.   if trimmed == "" then trimmed = nil end
  1328.   return trimmed
  1329.  
  1330. end
  1331.  
  1332.  
  1333.  
  1334. ---
  1335. -- Called once per script invocation, the purpose of this function is to populate the registry with variables and data for use by all threads.
  1336. -- @see  get_args, get_local_assignments_data
  1337.  
  1338. function script_init( )
  1339.  
  1340.   ---
  1341.   -- fields_meta is a table of patterns and captures and defines from which fields of a whois record to extract data.
  1342.   -- The fields are grouped into sets of RPSL-like objects with a key (e.g. rpsl, arin) which identifies the set.
  1343.   --
  1344.   -- ob_exist:   A pattern that is used to determine whether a record contains a set of objects.
  1345.   --             It does not have to be unique to the set of objects.  It does not require captures.
  1346.   -- ob_netnum:  A RPSL-like object containing fields describing the Address Assignment.  This object is mandatory for this script.
  1347.   -- Other optional objects include: ob_org (organisation), ob_role (role), ob_persn (person) and ob_cust (customer).
  1348.   --
  1349.   -- Each object table must contain the following:
  1350.   -- ob_start:  Pattern for the first field in the object and which marks the start of the object.  Does not require captures.
  1351.   -- ob_end:    Pattern for the last field in the object and which marks the end of the object.  Usually ends with "\r?\n\r?\n".
  1352.   --            Does not require captures.
  1353.   --
  1354.   -- The remaining key-value pairs for each object should conform to the following:
  1355.   -- key:    is a short name for the field in a whois record and which will be displayed in the scripts output to identify the field.
  1356.   -- value:  is a pattern for the field and contains a capture for the data required to be captured.
  1357.  
  1358.   nmap.registry.whois.fields_meta = {
  1359.     rpsl = {
  1360.       ob_exist =  "\r?\n?%s*[Ii]net6?num:%s*.-\r?\n",
  1361.       ob_netnum = {ob_start = "\r?\n?%s*[Ii]net6?num:%s*.-\r?\n",
  1362.             ob_end = "\r?\n%s*[Ss]ource:%s*.-\r?\n\r?\n",
  1363.             inetnum = "\r?\n%s*[Ii]net6?num:%s*(.-)\r?\n",
  1364.             netname = "\r?\n%s*[Nn]et[\-]-[Nn]ame:%s*(.-)\r?\n",
  1365.             nettype = "\r?\n%s*[Nn]et[\-]-[Tt]ype:%s*(.-)\r?\n",
  1366.             descr = "[Dd]escr:[^\r?\n][%s]*(.-)\r?\n",
  1367.             country = "\r?\n%s*[Cc]ountry:%s*(.-)\r?\n",
  1368.             status = "\r?\n%s*[Ss]tatus:%s*(.-)\r?\n",
  1369.             source = "\r?\n%s*[Ss]ource:%s*(.-)\r?\n"},
  1370.       ob_org = {  ob_start = "\r?\n%s*[Oo]rgani[sz]ation:%s*.-\r?\n",
  1371.             ob_end = "\r?\n%s*[Ss]ource:%s*.-\r?\n\r?\n",
  1372.             organisation = "\r?\n%s*[Oo]rgani[sz]ation:%s*(.-)\r?\n",
  1373.             orgname = "\r?\n%s*[Oo]rg[\-]-[Nn]ame:%s*(.-)\r?\n",
  1374.             descr = "[Dd]escr:[^\r?\n][%s]*(.-)\r?\n",
  1375.             email = "\r?\n%s*[Ee][\-]-[Mm]ail:%s*(.-)\r?\n"},
  1376.       ob_role = { ob_start = "\r?\n%s*[Rr]ole:%s*.-\r?\n",
  1377.             ob_end = "\r?\n%s*[Ss]ource:%s*.-\r?\n\r?\n",
  1378.             role = "\r?\n%s*[Rr]ole:%s*(.-)\r?\n",
  1379.             email = "\r?\n%s*[Ee][\-]-[Mm]ail:%s*(.-)\r?\n"},
  1380.       ob_persn = {  ob_start = "\r?\n%s*[Pp]erson:%s*.-\r?\n",
  1381.             ob_end = "\r?\n%s*[Ss]ource:%s*.-\r?\n\r?\n",
  1382.             person = "\r?\n%s*[Pp]erson:%s*(.-)\r?\n",
  1383.             email = "\r?\n%s*[Ee][\-]-[Mm]ail:%s*(.-)\r?\n"}  },
  1384.     arin = {
  1385.       ob_exist =  "\r?\n%s*[Nn]et[\-]-[Rr]ange:.-\r?\n",
  1386.       ob_netnum = {ob_start = "\r?\n%s*[Nn]et[\-]-[Rr]ange:.-\r?\n",
  1387.             ob_end = "\r?\n\r?\n",
  1388.             netrange = "\r?\n%s*[Nn]et[\-]-[Rr]ange:(.-)\r?\n",
  1389.             netname = "\r?\n%s*[Nn]et[\-]-[Nn]ame:(.-)\r?\n",
  1390.             nettype = "\r?\n%s*[Nn]et[\-]-[Tt]ype:(.-)\r?\n"},
  1391.       ob_org = {ob_start = "\r?\n%s*[Oo]rg[\-]-[Nn]ame:.-\r?\n",
  1392.             ob_end = "\r?\n\r?\n",
  1393.             orgname = "\r?\n%s*[Oo]rg[\-]-[Nn]ame:(.-)\r?\n",
  1394.             orgid = "\r?\n%s*[Oo]rg[\-]-[Ii][Dd]:(.-)\r?\n",
  1395.             stateprov = "\r?\n%s*[Ss]tate[\-]-[Pp]rov:(.-)\r?\n",
  1396.             country = "\r?\n%s*[Cc]ountry:(.-)\r?\n"},
  1397.       ob_cust = {ob_start = "\r?\n%s*[Cc]ust[\-]-[Nn]ame:.-\r?\n",
  1398.             ob_end = "\r?\n\r?\n",
  1399.             custname =  "\r?\n%s*[Cc]ust[\-]-[Nn]ame:(.-)\r?\n",
  1400.             stateprov = "\r?\n%s*[Ss]tate[\-]-[Pp]rov:(.-)\r?\n",
  1401.             country = "\r?\n%s*[Cc]ountry:(.-)\r?\n"},
  1402.       ob_persn = {ob_start = "\r?\n%s*[Oo]rg[\-]-[Tt]ech[\-]-[Nn]ame:.-\r?\n",
  1403.             ob_end = "\r?\n\r?\n",
  1404.             orgtechname =
  1405.             "\r?\n%s*[Oo]rg[\-]-[Tt]ech[\-]-[Nn]ame:(.-)\r?\n",
  1406.             orgtechemail =
  1407.             "\r?\n%s*[Oo]rg[\-]-[Tt]ech[\-]-[Ee][\-]-[Mm]ail:(.-)\r?\n"}  },
  1408.     lacnic = {
  1409.       ob_exist =  "\r?\n%s*[Ii]net6?num:%s*.-\r?\n",
  1410.       ob_netnum = {ob_start = "\r?\n%s*[Ii]net6?num:%s*.-\r?\n",
  1411.             ob_end = "\r?\n\r?\n",
  1412.             inetnum = "\r?\n%s*[Ii]net6?num:%s*(.-)\r?\n",
  1413.             owner = "\r?\n%s*[Oo]wner:%s*(.-)\r?\n",
  1414.             ownerid = "\r?\n%s*[Oo]wner[\-]-[Ii][Dd]:%s*(.-)\r?\n",
  1415.             responsible = "\r?\n%s*[Rr]esponsible:%s*(.-)\r?\n",
  1416.             country = "\r?\n%s*[Cc]ountry:%s*(.-)\r?\n",
  1417.             source = "\r?\n%s*[Ss]ource:%s*(.-)\r?\n"},
  1418.       ob_persn = {ob_start = "\r?\n%s*[Pp]erson:%s*.-\r?\n",
  1419.             ob_end = "\r?\n\r?\n",
  1420.             person = "\r?\n%s*[Pp]erson:%s*(.-)\r?\n",
  1421.             email = "\r?\n%s*[Ee][\-]-[Mm]ail:%s*(.-)\r?\n"}  },
  1422.     jpnic = {
  1423.       ob_exist =  "\r?\n%s*[Nn]etwork%s-[Ii]nformation:%s*.-\r?\n",
  1424.       ob_netnum = {ob_start = "\[[Nn]etwork%s*[Nn]umber\]%s*.-\r?\n",
  1425.             ob_end = "\r?\n\r?\n",
  1426.             inetnum = "\[[Nn]etwork%s*[Nn]umber\]%s*(.-)\r?\n",
  1427.             netname = "\[[Nn]etwork%s*[Nn]ame\]%s*(.-)\r?\n",
  1428.             orgname = "\[[Oo]rganization\]%s*(.-)\r?\n"} }
  1429.   }
  1430.  
  1431.   ---
  1432.   -- whoisdb defines the whois services this script is able to query and the script output produced for them.
  1433.   -- Each entry is a key-value pair where the key is a short name for the service and value is a table of definitions for that service.
  1434.   -- Note that there is defined here an entry for IANA which does not have a whois service.  The entry is defined to allow us to redirect to ARIN when
  1435.   -- IANA is referred to in a record.
  1436.   --
  1437.   -- Each service defined should contain the following:
  1438.   --
  1439.   -- id:             String. Matches the key for the service and is a short name for the service.
  1440.   -- hostname:       String. Hostname of the service.
  1441.   -- preflag:        String. Prepended to the target IP address sent in the whois query.
  1442.   -- postflag:       String. Appended to the target IP address sent in the whois query.
  1443.   -- longname:       Table of strings. Each is a lowercase official (or semi-official) name of the service.
  1444.   -- fieldreq:       Linked table entry.  The key identifying a table of a set of objects defined in fields_meta.
  1445.   --                 In its records each whois service displays a particular set of objects as defined here.
  1446.   -- smallnet_rule:  Linked table entry. The key of a pattern for the field defined in fields_meta which captures the Assignment Range.  This is an
  1447.   --                 optional entry and is used to extract the smallest (i.e. Most Specific) range from a record when more than one range is detailed.
  1448.   -- redirects:      Table of tables, containing strings.  Used to determine whether a record is referring to a different whois service by
  1449.   --                 searching for service specific information in certain fields of the record.
  1450.   --                 Each entry is a table thus: { "search_object", "search_field", "pattern" }
  1451.   --                 search_object: is the key name for a record object defined in fields_meta, in which to search.
  1452.   --                 search_field:  is the key name for a field of the object, the data of which to search.
  1453.   --                 pattern:       is typically the id or longname key names.
  1454.   --                 In the example: {"ob_org", "orgname", "longname"}, we cycle through each service defined in whoisdb and look for its longname in
  1455.   --                 the ob_org.orgname of the current record.
  1456.   -- output_short:   Table for each object to be displayed when Nmap verbosity is zero.  The first element of each table is the object name and the
  1457.   --                 second element is a table of fields to display.  The elements of the second may be field names, which are each output to a new
  1458.   --                 line, or tables containing field names which are output to the same line.
  1459.   -- output_long:    Table for each object to be displayed when Nmap verbosity is one or above.  The structure is the same as output_short.
  1460.   -- reg:            String name for the field in ob_netnum which captures the Assignment Range (e.g. "netrange", "inetnum"), the data of which is
  1461.   --                 cached in the registry.
  1462.   -- unordered:      Boolean.  Optional. True if the records from the service display an object other than ob_netnum as the first in the record (such
  1463.   --                 as at ARIN).  This flag is used to decide whether we should extract an object immediately before the relevant ob_netnum object
  1464.   --                 from a record.
  1465.  
  1466.   nmap.registry.whois.whoisdb = {
  1467.     arin = {
  1468.       id = "arin",
  1469.       hostname = "whois.arin.net", preflag = "+", postflag = "",
  1470.       longname = {"american registry for internet numbers"},
  1471.       fieldreq = nmap.registry.whois.fields_meta.arin,
  1472.       smallnet_rule = nmap.registry.whois.fields_meta.arin.ob_netnum.netrange,
  1473.       redirects = {
  1474.         {"ob_org", "orgname", "longname"},
  1475.         {"ob_org", "orgname", "id"},
  1476.         {"ob_org", "orgid", "id"} },
  1477.       output_short = {
  1478.         {"ob_netnum", {"netrange", "netname"}},
  1479.         {"ob_org", {"orgname", "orgid", {"country", "stateprov"}}}  },
  1480.       output_long = {
  1481.         {"ob_netnum", {"netrange", "netname"}},
  1482.         {"ob_org", {"orgname", "orgid", {"country", "stateprov"}}},
  1483.         {"ob_cust", {"custname", {"country", "stateprov"}}},
  1484.         {"ob_persn", {"orgtechname", "orgtechemail"}} },
  1485.       reg = "netrange",
  1486.       unordered = true
  1487.     },
  1488.     ripe = {
  1489.       id = "ripe",
  1490.       hostname = "whois.ripe.net", preflag = "-B", postflag = "",
  1491.       longname = {"ripe network coordination centre"},
  1492.       fieldreq = nmap.registry.whois.fields_meta.rpsl,
  1493.       smallnet_rule = nmap.registry.whois.fields_meta.rpsl.ob_netnum.inetnum,
  1494.       redirects = {
  1495.         {"ob_role", "role", "longname"},
  1496.         {"ob_org", "orgname", "id"},
  1497.         {"ob_org", "orgname", "longname"} },
  1498.       output_short = {
  1499.         {"ob_netnum", {"inetnum", "netname", "descr", "country"}},
  1500.         {"ob_org", {"orgname", "organisation", "descr", "email"}} },
  1501.       output_long = {
  1502.         {"ob_netnum", {"inetnum", "netname", "descr", "country"}},
  1503.         {"ob_org", {"orgname", "organisation", "descr", "email"}},
  1504.         {"ob_role", {"role", "email"}},
  1505.         {"ob_persn", {"person", "email"}} },
  1506.       reg = "inetnum"
  1507.     },
  1508.     apnic = {
  1509.       id = "apnic",
  1510.       hostname = "whois.apnic.net", preflag = "", postflag = "",
  1511.       longname = {"asia pacific network information centre"},
  1512.       fieldreq = nmap.registry.whois.fields_meta.rpsl,
  1513.       smallnet_rule = nmap.registry.whois.fields_meta.rpsl.ob_netnum.inetnum,
  1514.       redirects = {
  1515.         {"ob_netnum", "netname", "id"},
  1516.         {"ob_org", "orgname", "longname"},
  1517.         {"ob_role", "role", "longname"},
  1518.         {"ob_netnum", "source", "id"} },
  1519.       output_short = {
  1520.         {"ob_netnum", {"inetnum", "netname", "descr", "country"}},
  1521.         {"ob_org", {"orgname", "organisation", "descr", "email"}} },
  1522.       output_long = {
  1523.         {"ob_netnum", {"inetnum", "netname", "descr", "country"}},
  1524.         {"ob_org", {"orgname", "organisation", "descr", "email"}},
  1525.         {"ob_role", {"role", "email"}},
  1526.         {"ob_persn", {"person", "email"}} },
  1527.       reg = "inetnum"
  1528.     },
  1529.     lacnic = {
  1530.       id = "lacnic",
  1531.       hostname = "whois.lacnic.net", preflag = "", postflag = "",
  1532.       longname =
  1533.       {"latin american and caribbean ip address regional registry"},
  1534.       fieldreq = nmap.registry.whois.fields_meta.lacnic,
  1535.       smallnet_rule = nmap.registry.whois.fields_meta.lacnic.ob_netnum.inetnum,
  1536.       redirects = {
  1537.         {"ob_netnum", "ownerid", "id"},
  1538.         {"ob_netnum", "source", "id"} },
  1539.       output_short = {
  1540.         {"ob_netnum",
  1541.         {"inetnum", "owner", "ownerid", "responsible", "country"}}  },
  1542.       output_long = {
  1543.         {"ob_netnum",
  1544.         {"inetnum", "owner", "ownerid", "responsible", "country"}},
  1545.         {"ob_persn", {"person", "email"}} },
  1546.       reg = "inetnum"
  1547.     },
  1548.     afrinic = {
  1549.       id = "afrinic",
  1550.       hostname = "whois.afrinic.net", preflag = "-c", postflag = "",
  1551.       longname = {"african internet numbers registry",
  1552.             "african network information center"},
  1553.       fieldreq = nmap.registry.whois.fields_meta.rpsl,
  1554.       smallnet_rule = nmap.registry.whois.fields_meta.rpsl.ob_netnum.inetnum,
  1555.       redirects = {
  1556.         {"ob_org", "orgname", "longname"} },
  1557.       output_short = {
  1558.         {"ob_netnum", {"inetnum", "netname", "descr", "country"}},
  1559.         {"ob_org", {"orgname", "organisation", "descr", "email"}} },
  1560.       output_long = {
  1561.         {"ob_netnum", {"inetnum", "netname", "descr", "country"}},
  1562.         {"ob_org", {"orgname", "organisation", "descr", "email"}},
  1563.         {"ob_role", {"role", "email"}},
  1564.         {"ob_persn", {"person", "email"}} },
  1565.       reg = "inetnum"
  1566.     },--[[
  1567.     jpnic = {
  1568.       id = "jpnic",
  1569.       hostname = "whois.nic.ad.jp", preflag = "", postflag = "/e",
  1570.       longname = {"japan network information center"},
  1571.       fieldreq = nmap.registry.whois.fields_meta.jpnic,
  1572.       output_short = {
  1573.         {"ob_netnum", {"inetnum", "netname", "orgname"}}  },
  1574.       reg = "inetnum" },--]]
  1575.     iana = {  -- not actually a db but required here
  1576.       id = "iana", longname = {"internet assigned numbers authority"}
  1577.     }
  1578.   }
  1579.  
  1580.   nmap.registry.whois.m_none = {
  1581.     "\n%s*([Nn]o match found for[%s\+]*$addr)",
  1582.     "\n%s*([Uu]nallocated resource:%s*$addr)",
  1583.     "\n%s*([Rr]eserved:%s*$addr)",
  1584.     "\n[^\n]*([Nn]ot%s[Aa]ssigned[^\n]*$addr)",
  1585.     "\n%s*(No match!!)%s*\n",
  1586.     "(Invalid IP or CIDR block:%s*$addr)"
  1587.   }
  1588.   nmap.registry.whois.m_err = {
  1589.     "\n%s*([Aa]n [Ee]rror [Oo]ccured)%s*\n",
  1590.     "\n[^\n]*([Ee][Rr][Rr][Oo][Rr][^\n]*)\n"
  1591.   }
  1592.  
  1593.   nmap.registry.whois.remote_assignments_files = {}
  1594.     nmap.registry.whois.remote_assignments_files.ipv4 = {
  1595.       {
  1596.         remote_resource = "http://www.iana.org/assignments/ipv4-address-space/",
  1597.         local_resource = "ipv4-address-space",
  1598.         match_assignment = "^([\.%d]+/%d+)",
  1599.         match_service = "whois\.(%w+)\.net"
  1600.       }
  1601.     }
  1602.     nmap.registry.whois.remote_assignments_files.ipv6 = {
  1603.       --[[{
  1604.         remote_resource = "http://www.iana.org/assignments/ipv6-address-space",
  1605.         local_resource = "ipv6-address-space",
  1606.         match_assignment = "^([:%x]+/%d+)",
  1607.         match_service = "^[:%x]+/%d+%s*(%w+)"
  1608.       },--]]
  1609.       {
  1610.         remote_resource = "http://www.iana.org/assignments/ipv6-unicast-address-assignments",
  1611.         local_resource = "ipv6-unicast-address-assignments",
  1612.         match_assignment = "^([:%x]+/%d+)",
  1613.         match_service = "^[:%x]+/%d+%s*(%w+)"
  1614.       }
  1615.   }
  1616.  
  1617.   local err
  1618.  
  1619.   -- get and validate any --script-args
  1620.   get_args()
  1621.  
  1622.   -- mutex for each service
  1623.   nmap.registry.whois.mutex = {}
  1624.   for id, v in pairs( nmap.registry.whois.whoisdb ) do
  1625.     if id ~= "iana" then
  1626.       nmap.registry.whois.mutex[id] = nmap.mutex(nmap.registry.whois.whoisdb[id])
  1627.     end
  1628.   end
  1629.  
  1630.   -- get IANA assignments lists
  1631.   if nmap.registry.whois.using_local_assignments_file then
  1632.     nmap.registry.whois.local_assignments_data, err = get_local_assignments_data()
  1633.     if err then nmap.registry.whois.using_local_assignments_file = false end
  1634.   end
  1635.  
  1636.   nmap.registry.whois.init_done = true
  1637.  
  1638. end
  1639.  
  1640.  
  1641.  
  1642. ---
  1643. -- Parses the command line arguments passed to the script with --script-args.
  1644. -- Sets flags in the registry which threads read to determine certain behaviours.
  1645. -- Permitted args are 'nofile' - Prevents use of a list of assignments to determine which service to query,
  1646. -- 'nofollow' - Prevents following redirects found in records,
  1647. -- 'arin', 'ripe', 'apnic', etc. - Service id's, as defined in the whoisdb table in the registry (see script_init).
  1648.  
  1649. function get_args()
  1650.  
  1651.   if not nmap.registry.args then return end
  1652.  
  1653.   local args
  1654.   if ( nmap.registry.args.whois and nmap.registry.args.whois.whodb ) then
  1655.     args = nmap.registry.args.whois.whodb
  1656.   elseif nmap.registry.args.whodb then
  1657.     args = nmap.registry.args.whodb
  1658.   else return
  1659.   end
  1660.  
  1661.   if type( args ) ~= "string" or ( args == "" ) then return end
  1662.  
  1663.   local t = {}
  1664.   -- match words in args which may be whois dbs or other arguments
  1665.   for db in string.gmatch( args, "%w+" ) do
  1666.     if not nmap.registry.whois.whoisdb[db] then
  1667.       if ( db == "nofollow" ) then
  1668.         nmap.registry.whois.nofollow = true
  1669.       elseif ( db == "nocache" ) then
  1670.         nmap.registry.whois.using_cache = false
  1671.       elseif ( db == "nofile" ) then
  1672.         nmap.registry.whois.using_local_assignments_file = false
  1673.         stdnse.print_debug( 2, "%s: Not using local assignments data.", filename )
  1674.       end
  1675.     elseif not ( string.match( table.concat( t, " " ), db ) ) then
  1676.       -- we have a unique valid whois db
  1677.       t[#t+1] = db
  1678.     end
  1679.   end
  1680.  
  1681.   if ( #t > 0 ) and nmap.registry.whois.using_local_assignments_file then
  1682.     -- "nofile" was not explicitly supplied, but it is implied by supplying custom whoisdb_default_order
  1683.     nmap.registry.whois.using_local_assignments_file = false
  1684.     stdnse.print_debug(3, "%s: Not using local assignments data because custom whoisdb_default_order was supplied.", filename)
  1685.   end
  1686.  
  1687.   if ( #t > 1 ) and nmap.registry.whois.nofollow then
  1688.     -- using nofollow, we do not follow redirects and can only accept what we find as a record therefore we only accept the first db supplied
  1689.     t = {t[1]}
  1690.     stdnse.print_debug( 1, "%s: Too many args supplied with 'nofollow', only using %s.", filename, t[1] )
  1691.   end
  1692.  
  1693.   if ( #t > 0 ) then
  1694.     nmap.registry.whois.whoisdb_default_order = t
  1695.     stdnse.print_debug( 2, "%s: whoisdb_default_order: %s.", filename, table.concat( t, " " ) )
  1696.   end
  1697.  
  1698. end
  1699.  
  1700.  
  1701.  
  1702. ---
  1703. -- Makes IANA hosted assignments data available for lookups against that data.  In more detail it:
  1704. -- Caches a local copy of remote assignments data if copies do not currently exist or are out-of-date.
  1705. -- Checks whether the cached copies require updating and performs update as required.
  1706. -- Parses the cached copies and populates a table of lookup data which is returned to the caller.
  1707. -- Sets a flag in the registry to prevent use of the lookup data in the event of an error.
  1708. -- @return  Table of lookup data (or nil in case of an error).
  1709. -- @return  Nil or error message in case of an error.
  1710. -- @see     get_parentpath, file_exists, requires_updating, read_from_file, conditional_download,
  1711. --          write_to_file, parse_assignments
  1712.  
  1713. function get_local_assignments_data()
  1714.  
  1715.   if not next( nmap.registry.whois.remote_assignments_files ) then
  1716.     nmap.registry.whois.using_local_assignments_file = false
  1717.     return nil, "Error in get_local_assignments_data: Remote resources not defined in remote_assignments_files registry key"
  1718.   end
  1719.  
  1720.   -- get the directory path where cached files will be stored.
  1721.   local fetchfile = "nmap-services"
  1722.   local directory_path, err = get_parentpath( fetchfile )
  1723.   if err then
  1724.     stdnse.print_debug( 1, "%s: Nmap.fetchfile() failed to get a path to %s: %s.", filename, fetchfile, err )
  1725.     return nil, err
  1726.   end
  1727.  
  1728.   local ret = {}
  1729.  
  1730.   -- cache or update and parse each remote file for each address family
  1731.   for address_family, t in pairs( nmap.registry.whois.remote_assignments_files ) do
  1732.     for i, assignment_data_spec in ipairs( t ) do
  1733.  
  1734.       local update_required, modified_date, entity_tag, err
  1735.  
  1736.       -- do we have a cached file and does it need updating?
  1737.       local file, exists = directory_path .. assignment_data_spec.local_resource
  1738.       exists, err = file_exists( file )
  1739.       if not exists and err then
  1740.         stdnse.print_debug( 1, "%s: Error accessing %s: %s.", filename, file, err )
  1741.       elseif not exists then
  1742.         update_required = true
  1743.         stdnse.print_debug( 2, "%s: %s does not exist or is empty. Fetching it now...", filename, file )
  1744.       elseif exists then
  1745.         update_required, modified_date, entity_tag = requires_updating( file )
  1746.       end
  1747.  
  1748.       local file_content
  1749.  
  1750.       -- read an existing and up-to-date file into file_content.
  1751.       if exists and not update_required then
  1752.         stdnse.print_debug( 2, "%s: %s was cached less than %s ago. Reading...", filename, file, nmap.registry.whois.local_assignments_file_expiry )
  1753.         file_content = read_from_file( file )
  1754.       end
  1755.  
  1756.       -- cache or update and then read into file_content
  1757.       local http_response, write_success
  1758.       if update_required then
  1759.         http_response = ( conditional_download( assignment_data_spec.remote_resource, modified_date, entity_tag ) )
  1760.         if not http_response or type( http_response.status ) ~= "number" then
  1761.           stdnse.print_debug( 1, "%s: Failed whilst requesting %s.", filename, assignment_data_spec.remote_resource )
  1762.         elseif http_response.status == 200 then
  1763.           -- prepend our file header
  1764.           stdnse.print_debug( 2, "%s: Retrieved %s.", filename, assignment_data_spec.remote_resource )
  1765.           file_content = stdnse.strsplit( "\r?\n", http_response.body )
  1766.           table.insert( file_content, 1, "** Do Not Alter This Line or The Following Line **" )
  1767.           local hline = {}
  1768.           hline[#hline+1] = "<" .. os.time() .. ">"
  1769.           hline[#hline+1] = "<" .. http_response.header["last-modified"] .. ">"
  1770.           hline[#hline+1] = "<" .. http_response.header.etag .. ">"
  1771.           table.insert( file_content, 2, table.concat( hline ) )
  1772.           write_success, err = write_to_file( file, file_content )
  1773.           if err then
  1774.             stdnse.print_debug( 1, "%s: Error writing %s to %s: %s.", filename, assignment_data_spec.remote_resource, file, err )
  1775.           end
  1776.         elseif http_response.status == 304 then
  1777.           -- update our file header with a new timestamp
  1778.           stdnse.print_debug( 1, "%s: %s is up-to-date.", filename, file )
  1779.           file_content = read_from_file( file )
  1780.           file_content[2] = file_content[2]:gsub("^<[\-\+]?%d+>(.*)$", "<" .. os.time() .. ">%1")
  1781.           write_success, err = write_to_file( file, file_content )
  1782.           if err then
  1783.             stdnse.print_debug( 1, "%s: Error writing to %s: %s.", filename, file, err )
  1784.           end
  1785.         else
  1786.           stdnse.print_debug( 1, "%s: HTTP %s whilst requesting %s.", filename, http_response.status, assignment_data_spec.remote_resource )
  1787.         end
  1788.       end
  1789.  
  1790.  
  1791.       if file_content then
  1792.         -- Create a table for this address family (if there isn't one already).
  1793.         if not ret[address_family] then ret[address_family] = {} end
  1794.         -- Parse data and add to the table for this address family.
  1795.         local t
  1796.         t, err = parse_assignments( assignment_data_spec, file_content )
  1797.         if #t == 0 or err then
  1798.           -- good header, but bad file?  Kill the file!
  1799.           write_to_file( file, "" )
  1800.           stdnse.print_debug( 1, "%s: Problem with the data in %s.", filename, file )
  1801.         else
  1802.           for i, v in pairs( t ) do
  1803.             ret[address_family][#ret[address_family]+1] = v
  1804.           end
  1805.         end
  1806.       end
  1807.  
  1808.     end -- file
  1809.   end -- af
  1810.  
  1811.   -- If we decide to use more than one assignments file for ipv6 we may need to sort the resultant parsed list so that sub-assignments appear
  1812.   -- before their parent.  This is expensive, but it's worth doing to ensure the lookup process returns the correct service.
  1813.   -- table.sort( ret.ipv6, sort_assignments )
  1814.  
  1815.   -- final check for an empty table which we'll convert to nil
  1816.   for af, t in pairs( ret ) do
  1817.     if #t == 0 then
  1818.       ret[af] = nil
  1819.       stdnse.print_debug( 1, "%s: Cannot use local assignments file for address family %s.", filename, af )
  1820.     end
  1821.   end
  1822.  
  1823.   return ret
  1824.  
  1825. end
  1826.  
  1827.  
  1828.  
  1829. ---
  1830. -- Uses <code>nmap.fetchfile</code> to get the path of the parent directory of the supplied Nmap datafile filename.
  1831. -- @param fname  String - Filename of an Nmap datafile.
  1832. -- @return       String - The filepath of the directory containing the supplied filename including the trailing slash (or nil in case of an error).
  1833. -- @return       Nil or error message in case of an error.
  1834.  
  1835. function get_parentpath( fname )
  1836.  
  1837.   if type( fname ) ~= "string" or fname == "" then
  1838.     return nil, "Error in get_parentpath: Expected fname as a string."
  1839.   end
  1840.  
  1841.   local path = nmap.fetchfile( fname )
  1842.   if not path then
  1843.     return nil, "Error in get_parentpath: Call to fetchfile() failed."
  1844.   end
  1845.  
  1846.   path = path:sub( 1, path:len() - fname:len() )
  1847.   return path
  1848.  
  1849. end
  1850.  
  1851.  
  1852.  
  1853. ---
  1854. -- Given a filepath, checks for the existence of that file.
  1855. -- @param file  Path to a file.
  1856. -- @return      Boolean True if file exists and can be read or false if file does not exist or is empty or cannot be otherwise read.
  1857. -- @return      Nil or error message.  No error message if the file is empty or does not exist, only if the file cannot be read for some other reason.
  1858.  
  1859. function file_exists( file )
  1860.  
  1861.   local f, err, _ = io.open( file, "r" )
  1862.   if ( f and f:read() ) then
  1863.     f:close()
  1864.     return true, nil
  1865.   elseif f then
  1866.     f:close()
  1867.     return false, nil
  1868.   elseif not f and err:match("No such file or directory") then
  1869.     return false, nil
  1870.   elseif err then
  1871.     return false, err
  1872.   else
  1873.     return false, ( "unforseen error while checking " .. file )
  1874.   end
  1875.  
  1876. end
  1877.  
  1878.  
  1879.  
  1880. ---
  1881. -- Checks whether a cached file requires updating via HTTP.
  1882. -- The cached file should contain the following string on the second line: "<timestamp><Last-Modified-Date><Entity-Tag>".
  1883. -- where timestamp is number of seconds since epoch at the time the file was last cached and
  1884. -- Last-Modified-Date is an HTTP compliant date sting returned by an HTTP server at the time the file was last cached and
  1885. -- Entity-Tag is an HTTP Etag returned by an HTTP server at the time the file was last cached.
  1886. -- @param file  Filepath of the cached file.
  1887. -- @return      Boolean False if file does not require updating, true otherwise.
  1888. -- @return      nil or a valid modified-date (string).
  1889. -- @return      nil or a valid entity_tag (string).
  1890. -- @see         file_is_expired
  1891.  
  1892. function requires_updating( file )
  1893.  
  1894.   local last_cached, mod, etag, has_expired
  1895.  
  1896.   local f, err, _ = io.open( file, "r" )
  1897.   if not f then return true, nil end
  1898.  
  1899.   local _ = f:read("*line")
  1900.   local stamp = f:read("*line")
  1901.   f:close()
  1902.   if not stamp then return true, nil end
  1903.  
  1904.   last_cached, mod, etag = stamp:match( "<([^>]*)><([^>]*)><([^>]*)>" )
  1905.  
  1906.   if not ( last_cached or mod or etag ) then return true, nil end
  1907.  
  1908.   if not etag or not mod or not (
  1909.   mod:match( "%a%a%a,%s%d%d%s%a%a%a%s%d%d%d%d%s%d%d:%d%d:%d%d%s%u%u%u" )
  1910.   or
  1911.   mod:match( "%a*day,%d%d\-%a%a%a\-%d%d%s%d%d:%d%d:%d%d%s%u%u%u" )
  1912.   or
  1913.   mod:match( "%a%a%a%s%a%a%a%s%d?%d%s%d%d:%d%d:%d%d%s%d%d%d%d" )
  1914.   ) then
  1915.     return true, nil
  1916.   end
  1917.  
  1918.   -- Check whether the file was cached within local_assignments_file_expiry (registry value)
  1919.   has_expired = file_is_expired( last_cached )
  1920.  
  1921.   return has_expired, mod, etag
  1922.  
  1923. end
  1924.  
  1925.  
  1926.  
  1927. ---
  1928. -- Reads a file, line by line, into a table.
  1929. -- @param file  String representing a filepath.
  1930. -- @return      Table (array-style) of lines read from the file (or nil in case of an error).
  1931. -- @return      Nil or error message in case of an error.
  1932.  
  1933. function read_from_file( file )
  1934.  
  1935.   if type( file ) ~= "string" or file == "" then
  1936.     return nil, "Error in read_from_file: Expected file as a string."
  1937.   end
  1938.  
  1939.   local f, err, _ = io.open( file, "r" )
  1940.   if not f then
  1941.     stdnse.print_debug( 1, "%s: Error opening %s for reading: %s", filename, file, err )
  1942.     return nil, err
  1943.   end
  1944.  
  1945.   local line, ret = nil, {}
  1946.   while true do
  1947.     line = f:read()
  1948.     if not line then break end
  1949.     ret[#ret+1] = line
  1950.   end
  1951.  
  1952.   f:close()
  1953.  
  1954.   return ret
  1955.  
  1956. end
  1957.  
  1958.  
  1959.  
  1960. ---
  1961. -- Performs either an HTTP Conditional GET request if mod_date or e_tag is passed, or a plain GET request otherwise.
  1962. -- Will follow a single redirect for the remote resource.
  1963. -- @param url       String representing the full URL of the remote resource.
  1964. -- @param mod_date  String representing an HTTP date.
  1965. -- @param e_tag     String representing an HTTP entity tag.
  1966. -- @return          Table as per <code>http.request</code> or <code>nil</code> in case of a non-HTTP error.
  1967. -- @return          Nil or error message in case of an error.
  1968. -- @see             http.request
  1969.  
  1970. function conditional_download( url, mod_date, e_tag )
  1971.  
  1972.   if type( url ) ~= "string" or url == "" then
  1973.     return nil, "Error in conditional_download: Expected url as a string."
  1974.   end
  1975.  
  1976.   -- mod_date and e_tag allowed to be nil or a non-empty string
  1977.   if mod_date and ( type( mod_date ) ~= "string" or mod_date == "" ) then
  1978.     return nil, "Error in conditional_download: Expected mod_date as nil or as a non-empty string."
  1979.   end
  1980.   if e_tag and ( type( e_tag ) ~= "string" or e_tag == "" ) then
  1981.     return nil, "Error in conditional_download: Expected e_tag as nil or as a non-empty string."
  1982.   end
  1983.  
  1984.   -- use e_tag in preference to mod_date
  1985.   local request_options = {}
  1986.   request_options.header = {}
  1987.   if e_tag then
  1988.     request_options.header["If-None-Match"] = e_tag
  1989.   elseif mod_date then
  1990.     request_options.header["If-Modified-Since"] = mod_date
  1991.   end
  1992.   if not next( request_options.header ) then request_options = nil end
  1993.  
  1994.   local request_response = http.get_url( url, request_options )
  1995.  
  1996.   -- follow one redirection
  1997.   if request_response.status ~= 304 and ( tostring( request_response.status ):match( "30%d" ) and
  1998.   type( request_response.header.location ) == "string"  and request_response.header.location ~= "" ) then
  1999.     stdnse.print_debug( 2, "%s: HTTP Status:%d New Location: %s.", filename, request_response.status, request_response.header.location )
  2000.     request_response = http.get_url( request_response.header.location, request_options )
  2001.   end
  2002.  
  2003.   return request_response
  2004.  
  2005. end
  2006.  
  2007.  
  2008.  
  2009. ---
  2010. -- Writes the supplied content to file.
  2011. -- @param file     String representing a filepath (if it exists it will be overwritten).
  2012. -- @param content  String or table of data to write to file.  Empty string or table is permitted.
  2013. --                 A table will be written to file with each element of the table on a new line.
  2014. -- @return         Boolean True on success or nil in case of an error.
  2015. -- @return         Nil or error message in case of an error.
  2016.  
  2017. function write_to_file( file, content )
  2018.  
  2019.   if type( file ) ~= "string" or file == "" then
  2020.     return nil, "Error in write_to_file: Expected file as a string."
  2021.   end
  2022.   if type( content ) ~= "string" and type( content ) ~= "table" then
  2023.     return nil, "Error in write_to_file: Expected content as a table or string."
  2024.   end
  2025.  
  2026.   local f, err, _ = io.open( file, "w" )
  2027.   if not f then
  2028.     stdnse.print_debug( 1, "%s: Error opening %s for writing: %s.", filename, file, err )
  2029.     return nil, err
  2030.   end
  2031.  
  2032.   if ( type( content ) == "table" ) then
  2033.     content = table.concat( content, "\n" ) or ""
  2034.   end
  2035.   f:write( content )
  2036.  
  2037.   f:close()
  2038.  
  2039.   return true
  2040.  
  2041. end
  2042.  
  2043.  
  2044.  
  2045. ---
  2046. -- Converts raw data from an assignments file into a form optimised for lookups against that data.
  2047. -- @param address_family_spec  Table (assoc. array) containing patterns for extracting data.
  2048. -- @param table_of_lines       Table containing a line of data per table element.
  2049. -- @return                     Table - each element of the form { range = { first = data, last = data }, service = data } (or nil in case of an error).
  2050. -- @return                     Nil or error message in case of an error.
  2051.  
  2052. function parse_assignments( address_family_spec, table_of_lines )
  2053.  
  2054.   if #table_of_lines < 1 then
  2055.     return nil, "Error in parse_assignments: Expected table_of_lines as a non-empty table."
  2056.   end
  2057.  
  2058.   local mnetwork = address_family_spec.match_assignment
  2059.   local mservice = address_family_spec.match_service
  2060.  
  2061.   local ret = {}
  2062.  
  2063.   for i, line in ipairs( table_of_lines ) do
  2064.  
  2065.     net = line:match( mnetwork )
  2066.     if net then
  2067.       svc = line:match( mservice )
  2068.       if svc then svc = string.lower( svc ) end
  2069.       if not svc or ( svc == "iana" ) then
  2070.         svc = "arin"
  2071.       elseif not nmap.registry.whois.whoisdb[svc] then
  2072.         svc = "arin"
  2073.       end
  2074.       -- optimise the data
  2075.       local first_ip, last_ip, err = ipOps.get_ips_from_range( net )
  2076.       if not err then
  2077.         local t = { first = first_ip, last = last_ip }
  2078.         ret[#ret+1] = { range = t, service = svc }
  2079.       end
  2080.     end
  2081.  
  2082.   end
  2083.  
  2084.   return ret
  2085.  
  2086. end
  2087.  
  2088.  
  2089.  
  2090. ---
  2091. -- Checks the age of the supplied timestamp and compares it to the value of local_assignments_file_expiry.
  2092. -- @param time_string  String representing a timestamp (seconds since epoch).
  2093. -- @return             Boolean True if the period elapsed since the timestamp is longer than the value of local_assignments_file_expiry
  2094. --                     also returns true if the parameter is not of the expected type, otherwise returns false.
  2095. -- @see                sane_expiry_period
  2096.  
  2097. function file_is_expired( time_string )
  2098.  
  2099.   if type( time_string ) ~= "string" or time_string == "" then return true end
  2100.   local allowed_age = nmap.registry.whois.local_assignments_file_expiry
  2101.   if allowed_age == "" then return true end
  2102.  
  2103.   local cached_time = tonumber(time_string)
  2104.   if not cached_time then return true end
  2105.  
  2106.   local now_time = os.time()
  2107.   if now_time < cached_time then return true end
  2108.   if now_time > ( cached_time + sane_expiry_period( allowed_age ) ) then return true end
  2109.  
  2110.   return false
  2111.  
  2112. end
  2113.  
  2114.  
  2115.  
  2116. ---
  2117. -- Checks that the supplied string represents a period of time between 0 and 7 days.
  2118. -- @param period  String representing a period.
  2119. -- @return        Number representing the supplied period or a failsafe period in whole seconds.
  2120. -- @see           get_period
  2121.  
  2122. function sane_expiry_period( period )
  2123.  
  2124.   local sane_default_expiry = 57600 -- 16h
  2125.   local max_expiry = 604800 -- 7d
  2126.  
  2127.   period = get_period( period )
  2128.   if not period or ( period == "" ) then return sane_default_expiry end
  2129.  
  2130.   if period < max_expiry then return period end
  2131.   return max_expiry
  2132.  
  2133. end
  2134.  
  2135.  
  2136.  
  2137. ---
  2138. -- Converts a string representing a period of time made up of a quantity and a unit such as "24h"
  2139. -- into whole seconds.
  2140. -- @param period  String combining a quantity and a unit of time.
  2141. --                Acceptable units are days (D or d), hours (H or h), minutes (M or m) and seconds (S or s).
  2142. --                If a unit is not supplied or not one of the above acceptable units, it is assumed to be seconds.
  2143. --                Negative or fractional periods are permitted.
  2144. -- @return        Number representing the supplied period in whole seconds (or nil in case of an error).
  2145.  
  2146. function get_period( period )
  2147.  
  2148.   if type( period ) ~= string or ( period == "" ) then return nil end
  2149.   local quant, unit = period:match( "(\-?\+?%d*\.?%d*)([SsMmHhDd]?)" )
  2150.   if not ( tonumber( quant ) ) then return nil end
  2151.  
  2152.   if ( string.lower( unit ) == "m" ) then
  2153.     unit = 60
  2154.   elseif ( string.lower( unit ) == "h" ) then
  2155.     unit = 3600
  2156.   elseif ( string.lower( unit ) == "d" ) then
  2157.     unit = 86400
  2158.   else
  2159.     -- seconds and catch all
  2160.     unit = 1
  2161.   end
  2162.  
  2163.   return ( math.modf( quant * unit ) )
  2164.  
  2165. end
  2166.  
  2167.  
  2168.  
  2169. --
  2170. -- Passed to <code>table.sort</code>, will sort a table of IP assignments such that sub-assignments appear before their parent.
  2171. -- This function is not in use at the moment (see get_local_assignments_data) and will not appear in nse documentation.
  2172. -- @param first   Table { range = { first = IP_addr, last = IP_addr } }
  2173. -- @param second  Table { range = { first = IP_addr, last = IP_addr } }
  2174. -- @return        Boolean True if the tables are already in the correct order, otherwise false.
  2175.  
  2176. function sort_assignments( first, second )
  2177.  
  2178.   local f_lo, f_hi = first.range.first, first.range.last
  2179.   local s_lo, s_hi = second.range.first, second.range.last
  2180.  
  2181.   if ipOps.compare_ip( f_lo, "gt", s_lo ) then return false end
  2182.   if ipOps.compare_ip( f_lo, "le", s_lo ) and ipOps.compare_ip( f_hi, "ge", s_hi ) then
  2183.     return false
  2184.   end
  2185.  
  2186.   return true
  2187.  
  2188. end
  2189.