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