diff --git a/nselib/dns.lua b/nselib/dns.lua
index 05bae112c..85462e596 100644
--- a/nselib/dns.lua
+++ b/nselib/dns.lua
@@ -31,6 +31,7 @@
module(... or "dns", package.seeall)
+require("bit")
require("ipOps")
require("stdnse")
@@ -955,29 +956,44 @@ decoder[types.SOA] = function(entry, data, pos)
= bin.unpack(">I5", data, np)
end
--- Decodes NSEC records, puts result in entry.NSEC.
+-- An iterator that returns the positions of nonzero bits in the given binary
+-- string.
+local function bit_iter(bits)
+ return coroutine.wrap(function()
+ for i = 1, #bits do
+ local n = string.byte(bits, i)
+ local j = 0
+ local mask = 0x80
+
+ while mask > 0 do
+ if bit.band(n, mask) ~= 0 then
+ coroutine.yield((i - 1) * 8 + j)
+ end
+ j = j + 1
+ mask = bit.rshift(mask, 1)
+ end
+ end
+ end)
+end
+
+-- Decodes NSEC records, puts result in entry.NSEC. See RFC 4034,
+-- section 4.
--
-- entry.NSEC has the fields dname,
--- NSEC, name, WinBlockNo,
--- bmplength, bin, and types.
+-- name, and types.
-- @param entry RR in packet.
-- @param data Complete encoded DNS packet.
-- @param pos Position in packet after RR.
decoder[types.NSEC] = function (entry, data, pos)
local np = pos - #entry.data
+ local block_num, type_bitmap
entry.NSEC = {}
entry.NSEC.dname = entry.dname
- entry.NSEC.NSEC = true
np, entry.NSEC.name = decStr(data, np)
- np, entry.NSEC.WinBlockNo, entry.NSEC.bmplength = bin.unpack(">CC", data, np)
- np, entry.NSEC.bin = bin.unpack("B".. entry.NSEC.bmplength, data, np)
+ np, block_num, type_bitmap = bin.unpack(">Cp", data, np)
entry.NSEC.types = {}
- for i=1, string.len(entry.NSEC.bin) do
- local bit = string.sub(entry.NSEC.bin,i,i)
- if bit == "1" then
- --the first bit represents window block 0 hence -1
- table.insert(entry.NSEC.types, (entry.NSEC.WinBlockNo*256+i-1))
- end
+ for i in bit_iter(type_bitmap) do
+ entry.NSEC.types[(block_num - 1) * 256 + i] = true
end
end