diff --git a/CHANGELOG b/CHANGELOG index 1e47fc10c..cfdafa81e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added ipv6 support to the wsdd, dnssd and upnp libraries. Applied + patch from Dan Miller that fixes errors in processing and sorting ipv6 + addresses in scripts using these libraries. [Daniel Miller, Patrik] + o [NSE] Added minimal Service Location Protocol (SLP) library and the script broadcast-novell-locate that detects servers running eDirectory. [Patrik] diff --git a/nselib/dnssd.lua b/nselib/dnssd.lua index d06142e67..fcc8898ed 100644 --- a/nselib/dnssd.lua +++ b/nselib/dnssd.lua @@ -41,31 +41,17 @@ module(... or "dnssd", package.seeall) require 'dns' require 'target' +require 'ipOps' Util = { - - --- Converts a string ip to a numeric value suitable for comparing - -- - -- @param ip string containing the ip to convert - -- @return number containing the converted ip - ipToNumber = function(ip) - local o1, o2, o3, o4 = ip:match("^(%d*)%.(%d*)%.(%d*)%.(%d*)$") - return (256^3) * o1 + (256^2) * o2 + (256^1) * o3 + (256^0) * o4 - end, --- Compare function used for sorting IP-addresses -- -- @param a table containing first item -- @param b table containing second item - -- @return true if the port of a is less than the port of b + -- @return true if a is less than b ipCompare = function(a, b) - local ip_a = Util.ipToNumber(a.name) or 0 - local ip_b = Util.ipToNumber(b.name) or 0 - - if ( tonumber(ip_a) < tonumber(ip_b) ) then - return true - end - return false + return ipOps.compare_ip(a, "lt", b) end, --- Function used to compare discovered DNS services so they can be sorted @@ -229,7 +215,10 @@ Comm = { if status then address = ip end status, ipv6 = Comm.getRecordType( dns.types.AAAA, response, false ) - if status then address = address .. " " .. ipv6 end + if status then + address = address or "" + address = address .. " " .. ipv6 + end status, txt = Comm.getRecordType( dns.types.TXT, response, true ) if status then @@ -337,7 +326,8 @@ Helper = { local status, response local mcast = self.mcast local port = self.port or 5353 - local host = mcast and "224.0.0.251" or self.host + local family = nmap.address_family() + local host = mcast and (family=="inet6" and "ff02::fb" or "224.0.0.251") or self.host local service = service or stdnse.get_script_args('dnssd.services') if ( not(service) ) then diff --git a/nselib/ipOps.lua b/nselib/ipOps.lua index 31befc361..8e6bc31c0 100644 --- a/nselib/ipOps.lua +++ b/nselib/ipOps.lua @@ -11,6 +11,7 @@ local tonumber = tonumber local stdnse = require "stdnse" local bit = require "bit" +local bin = require "bin" module ( "ipOps" ) @@ -126,7 +127,7 @@ end -- -- Note: IPv6 addresses are not supported. Currently, numbers in NSE are -- limited to 10^14, and consequently not all IPv6 addresses can be --- represented. +-- represented. Consider using ip_to_str for IPv6 addresses. -- @param ip String representing an IPv4 address. Shortened notation is -- permitted. -- @usage @@ -211,7 +212,8 @@ end --- --- Compares two IP addresses (from the same address family). +-- Compares two IP addresses. When comparing addresses from different families, +-- IPv4 addresses will sort before IPv6 addresses. -- @param left String representing an IPv4 or IPv6 address. Shortened -- notation is permitted. -- @param op A comparison operator which may be one of the following @@ -231,47 +233,36 @@ compare_ip = function( left, op, right ) return nil, "Error in ipOps.compare_ip: Expected IP address as a string." end - if ( left:match( ":" ) and not right:match( ":" ) ) or ( not left:match( ":" ) and right:match( ":" ) ) then - return nil, "Error in ipOps.compare_ip: IP addresses must be from the same address family." - end - - if op == "lt" or op == "le" then - left, right = right, left - elseif op ~= "eq" and op ~= "ge" and op ~= "gt" then - return nil, "Error in ipOps.compare_ip: Invalid Operator." - end - local err ={} - left, err[#err+1] = ip_to_bin( left ) - right, err[#err+1] = ip_to_bin( right ) + left, err[#err+1] = ip_to_str( left ) + right, err[#err+1] = ip_to_str( right ) if #err > 0 then return nil, table.concat( err, " " ) end - if # left ~= # right then - -- shouldn't happen... - return nil, "Error in ipOps.compare_ip: Binary IP addresses were of different lengths." + if #left > #right then + left = bin.pack( "CA", 0x06, left ) + right = bin.pack( "CA", 0x04, right ) + elseif #right > #left then + right = bin.pack( "CA", 0x06, right ) + left = bin.pack( "CA", 0x04, left ) end - -- equal? - if ( op == "eq" or op == "le" or op == "ge" ) and left == right then - return true - elseif op == "eq" then - return false + if ( op == "eq" ) then + return ( left == right ) + elseif ( op == "ne" ) then + return ( left ~= right ) + elseif ( op == "le" ) then + return ( left <= right ) + elseif ( op == "ge" ) then + return ( left >= right ) + elseif ( op == "lt" ) then + return ( left < right ) + elseif ( op == "gt" ) then + return ( left > right ) end - -- starting from the leftmost bit, subtract the bit in right from the bit in left - local compare - for i = 1, # left , 1 do - compare = tonumber( string.sub( left, i, i ) ) - tonumber( string.sub( right, i, i ) ) - if compare == 1 then - return true - elseif compare == -1 then - return false - end - end - return false - + return nil, "Error in ipOps.compare_ip: Invalid Operator." end @@ -327,12 +318,15 @@ end -- the IPv4 portion is shortened and does not contain a dot, in which case the -- address will be treated as IPv6. -- @param ip String representing an IPv4 or IPv6 address in shortened or full notation. +-- @param family String representing the address family to expand to. Only +-- affects IPv4 addresses when "inet6" is provided, causing the function to +-- return an IPv4-mapped IPv6 address. -- @usage -- local ip = ipOps.expand_ip( "2001::" ) -- @return String representing a fully expanded IPv4 or IPv6 address (or -- nil in case of an error). -- @return String error message in case of an error. -expand_ip = function( ip ) +expand_ip = function( ip, family ) local err if type( ip ) ~= "string" or ip == "" then @@ -355,7 +349,18 @@ expand_ip = function( ip ) while #octets < 4 do octets[#octets+1] = "0" end - return ( table.concat( octets, "." ) ) + if family == "inet6" then + return ( table.concat( { 0,0,0,0,0,"ffff", + stdnse.tohex( 256*octets[1]+octets[2] ), + stdnse.tohex( 256*octets[3]+octets[4] ) + }, ":" ) ) + else + return ( table.concat( octets, "." ) ) + end + end + + if family ~= nil and family ~= "inet6" then + return nil, "Error in ipOps.expand_ip: Cannot convert IPv6 address to IPv4" end if ip:match( "[^\.:%x]" ) then @@ -498,6 +503,39 @@ get_last_ip = function( ip, prefix ) end +--- +-- Converts an IP address into an opaque string. +-- @param ip String representing an IPv4 or IPv6 address. +-- @param family (optional) Address family to convert to. "ipv6" converts IPv4 +-- addresses to IPv4-mapped IPv6. +-- @usage +-- opaque = ipOps.ip_to_str( "192.168.3.4" ) +-- @return 4- or 16-byte string representing IP address (or nil +-- in case of an error). +-- @return String error message in case of an error +ip_to_str = function( ip, family ) + local err + + ip, err = expand_ip( ip, family ) + if err then return nil, err end + + local t = {} + + if not ip:match( ":" ) then + -- ipv4 string + for octet in string.gmatch( ip, "%d+" ) do + t[#t+1] = bin.pack( ">C", tonumber(octet) ) + end + else + -- ipv6 string + for hdt in string.gmatch( ip, "%x+" ) do + t[#t+1] = bin.pack( ">S", tonumber(hdt, 16) ) + end + end + + + return table.concat( t ) +end --- diff --git a/nselib/upnp.lua b/nselib/upnp.lua index 21d5f2436..2c1c68c74 100644 --- a/nselib/upnp.lua +++ b/nselib/upnp.lua @@ -40,30 +40,16 @@ require("target") require("http") Util = { - - --- Converts a string ip to a numeric value suitable for comparing - -- - -- @param ip string containing the ip to convert - -- @return number containing the converted ip - ipToNumber = function(ip) - local o1, o2, o3, o4 = ip:match("^(%d*)%.(%d*)%.(%d*)%.(%d*)$") - return (256^3) * o1 + (256^2) * o2 + (256^1) * o3 + (256^0) * o4 - end, --- Compare function used for sorting IP-addresses -- -- @param a table containing first item -- @param b table containing second item - -- @return true if the port of a is less than the port of b + -- @return true if a is less than b ipCompare = function(a, b) - local ip_a = Util.ipToNumber(a.name) - local ip_b = Util.ipToNumber(b.name) - if ( tonumber(ip_a) < tonumber(ip_b) ) then - return true - end - return false - end - + return ipOps.compare_ip(a, "lt", b) + end, + } Comm = { @@ -293,7 +279,8 @@ Comm = { setMulticast = function( self, mcast ) assert( type(mcast)=="boolean", "mcast has to be either true or false") self.mcast = mcast - self.host = "239.255.255.250" + local family = nmap.address_family() + self.host = (family=="inet6" and "FF02::C" or "239.255.255.250") self.port = 1900 end, @@ -351,4 +338,4 @@ Helper = { return status, response end, -} \ No newline at end of file +} diff --git a/nselib/wsdd.lua b/nselib/wsdd.lua index d3265edfa..2f12d990f 100644 --- a/nselib/wsdd.lua +++ b/nselib/wsdd.lua @@ -322,8 +322,10 @@ Helper = { -- @param mcast boolean true if multicast is to be used, false otherwise setMulticast = function( self, mcast ) assert( type(mcast)=="boolean", "mcast has to be either true or false") + local family = nmap.address_family() self.mcast = mcast - self.host, self.port = "239.255.255.250", 3702 + self.host = (family=="inet6" and "FF02::C" or "239.255.255.250") + self.port = 3702 end, --- Sets the timeout for socket reads diff --git a/scripts/ntp-monlist.nse b/scripts/ntp-monlist.nse index f8f5fbfe9..7438720fb 100644 --- a/scripts/ntp-monlist.nse +++ b/scripts/ntp-monlist.nse @@ -1059,8 +1059,9 @@ function output_ips(t) end end - -- IPv6 - for now, no sorting. + -- IPv6 -- Rows are allowed to be 71 chars wide + table.sort(t['6'], function(a,b) return ipOps.compare_ip(a, "lt", b) end) local i = 1 local limit = #t['6'] while i <= limit do