diff --git a/nselib/dns.lua b/nselib/dns.lua
index 7fb8a76a8..872fc52a7 100644
--- a/nselib/dns.lua
+++ b/nselib/dns.lua
@@ -437,6 +437,31 @@ function reverse(ip)
return table.concat(ipReverse, ".") .. arpa
end
+local fetcher = function(typename, callback)
+ return function(dec, retAll)
+ local answers = {}
+ for _, ans in ipairs(dec) do
+ if ans.dtype == types[typename] then
+ if not retAll then
+ return true, callback(ans)
+ end
+ table.insert(answers, callback(ans))
+ end
+ end
+ if #answers == 0 then
+ stdnse.debug2("dns.answerFetcher found no records of the required type: %s", typename)
+ return false, "No Answers"
+ end
+ return true, answers
+ end
+end
+
+local getattr = function(attr)
+ return function(obj)
+ return obj[attr]
+ end
+end
+
-- Table for answer fetching functions.
local answerFetcher = {}
@@ -447,22 +472,18 @@ local answerFetcher = {}
-- @return String first dns TXT record or Table of TXT records or String Error message.
answerFetcher[types.TXT] = function(dec, retAll)
local answers = {}
- if not retAll and dec.answers[1].data then
- return true, string.sub(dec.answers[1].data, 2)
- elseif not retAll then
- stdnse.debug1("dns.answerFetcher found no records of the required type: TXT")
- return false, "No Answers"
- else
- for _, v in ipairs(dec.answers) do
- if v.TXT and v.TXT.text then
- for _, v in ipairs( v.TXT.text ) do
- table.insert(answers, v)
+ for _, v in ipairs(dec) do
+ if v.TXT and v.TXT.text then
+ for _, v in ipairs( v.TXT.text ) do
+ if not retAll then
+ return true, v
end
+ table.insert(answers, v)
end
end
end
if #answers == 0 then
- stdnse.debug1("dns.answerFetcher found no records of the required type: TXT")
+ stdnse.debug2("dns.answerFetcher found no records of the required type: TXT")
return false, "No Answers"
end
return true, answers
@@ -473,47 +494,14 @@ end
-- @param retAll If true, return all entries, not just the first.
-- @return True if one or more answers of the required type were found - otherwise false.
-- @return String first dns A record or Table of A records or String Error message.
-answerFetcher[types.A] = function(dec, retAll)
- local answers = {}
- for _, ans in ipairs(dec.answers) do
- if ans.dtype == types.A then
- if not retAll then
- return true, ans.ip
- end
- table.insert(answers, ans.ip)
- end
- end
- if not retAll or #answers == 0 then
- stdnse.debug1("dns.answerFetcher found no records of the required type: A")
- return false, "No Answers"
- end
- return true, answers
-end
-
+answerFetcher[types.A] = fetcher("A", getattr("ip"))
-- Answer fetcher for CNAME records.
-- @param dec Decoded DNS response.
-- @param retAll If true, return all entries, not just the first.
-- @return True if one or more answers of the required type were found - otherwise false.
-- @return String first Domain entry or Table of domain entries or String Error message.
-answerFetcher[types.CNAME] = function(dec, retAll)
- local answers = {}
- if not retAll and dec.answers[1].domain then
- return true, dec.answers[1].domain
- elseif not retAll then
- stdnse.debug1("dns.answerFetcher found no records of the required type: NS, PTR or CNAME")
- return false, "No Answers"
- else
- for _, v in ipairs(dec.answers) do
- if v.domain then table.insert(answers, v.domain) end
- end
- end
- if #answers == 0 then
- stdnse.debug1("dns.answerFetcher found no records of the required type: NS, PTR or CNAME")
- return false, "No Answers"
- end
- return true, answers
-end
+answerFetcher[types.CNAME] = fetcher("CNAME", getattr("domain"))
-- Answer fetcher for MX records.
-- @param dec Decoded DNS response.
@@ -522,32 +510,9 @@ end
-- @return String first dns MX record or Table of MX records or String Error message.
-- Note that the format of a returned MX answer is "preference:hostname:IPaddress" where zero
-- or more IP addresses may be present.
-answerFetcher[types.MX] = function(dec, retAll)
- local mx, ip, answers = {}, {}, {}
- for _, ans in ipairs(dec.answers) do
- if ans.MX then mx[#mx+1] = ans.MX end
- if not retAll then break end
- end
- if #mx == 0 then
- stdnse.debug1("dns.answerFetcher found no records of the required type: MX")
- return false, "No Answers"
- end
- for _, add in ipairs(dec.add) do
- if ip[add.dname] then table.insert(ip[add.dname], add.ip)
- else ip[add.dname] = {add.ip} end
- end
- for _, mxrec in ipairs(mx) do
- if ip[mxrec.server] then
- table.insert( answers, ("%s:%s:%s"):format(mxrec.pref or "-", mxrec.server or "-", table.concat(ip[mxrec.server], ":")) )
- if not retAll then return true, answers[1] end
- else
- -- no IP ?
- table.insert( answers, ("%s:%s"):format(mxrec.pref or "-", mxrec.server or "-") )
- if not retAll then return true, answers[1] end
- end
- end
- return true, answers
-end
+answerFetcher[types.MX] = fetcher("MX", function(ans)
+ return ("%s:%s"):format(ans.pref, ans.server)
+ end)
-- Answer fetcher for SRV records.
-- @param dec Decoded DNS response.
@@ -556,23 +521,9 @@ end
-- @return String first dns SRV record or Table of SRV records or String Error message.
-- Note that the format of a returned SRV answer is "priority:weight:port:target" where zero
-- or more IP addresses may be present.
-answerFetcher[types.SRV] = function(dec, retAll)
- local srv, ip, answers = {}, {}, {}
- for _, ans in ipairs(dec.answers) do
- if ans.dtype == types.SRV then
- if not retAll then
- return true, ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target )
- end
- table.insert( answers, ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target ) )
- end
- end
- if #answers == 0 then
- stdnse.debug1("dns.answerFetcher found no records of the required type: SRV")
- return false, "No Answers"
- end
-
- return true, answers
-end
+answerFetcher[types.SRV] = fetcher("SRV", function(ans)
+ return ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target )
+ end)
-- Answer fetcher for NSEC records.
-- @param dec Decoded DNS response.
@@ -580,22 +531,9 @@ end
-- @return True if one or more answers of the required type were found - otherwise false.
-- @return String first dns NSEC record or Table of NSEC records or String Error message.
-- Note that the format of a returned NSEC answer is "name:dname:types".
-answerFetcher[types.NSEC] = function(dec, retAll)
- local nsec, answers = {}, {}
- for _, auth in ipairs(dec.auth) do
- if auth.NSEC then nsec[#nsec+1] = auth.NSEC end
- if not retAll then break end
- end
- if #nsec == 0 then
- stdnse.debug1("dns.answerFetcher found no records of the required type: NSEC")
- return false, "No Answers"
- end
- for _, nsecrec in ipairs(nsec) do
- table.insert( answers, ("%s:%s:%s"):format(nsecrec.name or "-", nsecrec.dname or "-", table.concat(nsecrec.types, ":") or "-"))
- end
- if not retAll then return true, answers[1] end
- return true, answers
-end
+answerFetcher[types.NSEC] = fetcher("NSEC", function(ans)
+ return ("%s:%s:%s"):format(ans.name or "-", ans.dname or "-", table.concat(ans.types, ":") or "-")
+ end)
-- Answer fetcher for NS records.
-- @name answerFetcher[types.NS]
@@ -603,7 +541,7 @@ end
-- @param dec Decoded DNS response.
-- @return True if one or more answers of the required type were found - otherwise false.
-- @return String first Domain entry or Table of domain entries or String Error message.
-answerFetcher[types.NS] = answerFetcher[types.CNAME]
+answerFetcher[types.NS] = fetcher("NS", getattr("domain"))
-- Answer fetcher for PTR records.
-- @name answerFetcher[types.PTR]
@@ -612,31 +550,30 @@ answerFetcher[types.NS] = answerFetcher[types.CNAME]
-- @param retAll If true, return all entries, not just the first.
-- @return True if one or more answers of the required type were found - otherwise false.
-- @return String first Domain entry or Table of domain entries or String Error message.
-answerFetcher[types.PTR] = answerFetcher[types.CNAME]
+answerFetcher[types.PTR] = fetcher("PTR", getattr("domain"))
-- Answer fetcher for AAAA records.
-- @param dec Decoded DNS response.
-- @param retAll If true, return all entries, not just the first.
-- @return True if one or more answers of the required type were found - otherwise false.
-- @return String first dns AAAA record or Table of AAAA records or String Error message.
-answerFetcher[types.AAAA] = function(dec, retAll)
- local answers = {}
- for _, ans in ipairs(dec.answers) do
- if ans.dtype == types.AAAA then
- if not retAll then
- return true, ans.ipv6
- end
- table.insert(answers, ans.ipv6)
+answerFetcher[types.AAAA] = fetcher("AAAA", getattr("ipv6"))
+
+local function generic_finder(dtype, dec, section, retAll)
+ if (#dec[section] > 0) then
+ if answerFetcher[dtype] then
+ return answerFetcher[dtype](dec[section], retAll)
+ else
+ stdnse.debug1("dns.lua: no answerFetcher for dtype %s", tostring(dtype))
+ return false, "Unable to handle response"
end
- end
- if not retAll or #answers == 0 then
- stdnse.debug1("dns.answerFetcher found no records of the required type: AAAA")
+ elseif (dec.flags.RC3 and dec.flags.RC4) then
+ return false, "No Such Name"
+ else
return false, "No Answers"
end
- return true, answers
end
-
---Calls the answer fetcher for dtype or returns an error code in
-- case of a "no such name" error.
--
@@ -646,126 +583,7 @@ end
-- @return True if one or more answers of the required type were found - otherwise false.
-- @return Answer according to the answer fetcher for dtype or an Error message.
function findNiceAnswer(dtype, dec, retAll)
- if (#dec.answers > 0) then
- if answerFetcher[dtype] then
- return answerFetcher[dtype](dec, retAll)
- else
- stdnse.debug1("dns.findNiceAnswer() does not have an answerFetcher for dtype %s", tostring(dtype))
- return false, "Unable to handle response"
- end
- elseif (dec.flags.RC3 and dec.flags.RC4) then
- return false, "No Such Name"
- else
- stdnse.debug1("dns.findNiceAnswer() found zero answers in a response, but got an unexpected flags.replycode")
- return false, "No Answers"
- end
-end
-
--- Table for additional fetching functions.
--- Some servers return their answers in the additional section. The
--- findNiceAdditional function with its relevant additionalFetcher functions
--- addresses this. This unfortunately involved some code duplication (because
--- of current design of the dns library) from the answerFetchers to the
--- additionalFetchers.
-local additionalFetcher = {}
-
--- Additional fetcher for TXT records.
--- @param dec Decoded DNS response.
--- @param retAll If true, return all entries, not just the first.
--- @return True if one or more answers of the required type were found - otherwise false.
--- @return String first dns TXT record or Table of TXT records or String Error message.
-additionalFetcher[types.TXT] = function(dec, retAll)
- local answers = {}
- if not retAll and dec.add[1].data then
- return true, string.sub(dec.add[1].data, 2)
- elseif not retAll then
- stdnse.debug1("dns.additionalFetcher found no records of the required type: TXT")
- return false, "No Answers"
- else
- for _, v in ipairs(dec.add) do
- if v.TXT and v.TXT.text then
- for _, v in ipairs( v.TXT.text ) do
- table.insert(answers, v)
- end
- end
- end
- end
- if #answers == 0 then
- stdnse.debug1("dns.answerFetcher found no records of the required type: TXT")
- return false, "No Answers"
- end
- return true, answers
-end
-
--- Additional fetcher for A records
--- @param dec Decoded DNS response.
--- @param retAll If true, return all entries, not just the first.
--- @return True if one or more answers of the required type were found - otherwise false.
--- @return String first dns A record or Table of A records or String Error message.
-additionalFetcher[types.A] = function(dec, retAll)
- local answers = {}
- for _, ans in ipairs(dec.add) do
- if ans.dtype == types.A then
- if not retAll then
- return true, ans.ip
- end
- table.insert(answers, ans.ip)
- end
- end
- if not retAll or #answers == 0 then
- stdnse.debug1("dns.answerFetcher found no records of the required type: A")
- return false, "No Answers"
- end
- return true, answers
-end
-
-
--- Additional fetcher for SRV records.
--- @param dec Decoded DNS response.
--- @param retAll If true, return all entries, not just the first.
--- @return True if one or more answers of the required type were found - otherwise false.
--- @return String first dns SRV record or Table of SRV records or String Error message.
--- Note that the format of a returned SRV answer is "priority:weight:port:target" where zero
--- or more IP addresses may be present.
-additionalFetcher[types.SRV] = function(dec, retAll)
- local srv, ip, answers = {}, {}, {}
- for _, ans in ipairs(dec.add) do
- if ans.dtype == types.SRV then
- if not retAll then
- return true, ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target )
- end
- table.insert( answers, ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target ) )
- end
- end
- if #answers == 0 then
- stdnse.debug1("dns.answerFetcher found no records of the required type: SRV")
- return false, "No Answers"
- end
-
- return true, answers
-end
-
-
--- Additional fetcher for AAAA records.
--- @param dec Decoded DNS response.
--- @param retAll If true, return all entries, not just the first.
--- @return True if one or more answers of the required type were found - otherwise false.
--- @return String first dns AAAA record or Table of AAAA records or String Error message.
-additionalFetcher[types.AAAA] = function(dec, retAll)
- local answers = {}
- for _, ans in ipairs(dec.add) do
- if ans.dtype == types.AAAA then
- if not retAll then
- return true, ans.ipv6
- end
- table.insert(answers, ans.ipv6)
- end
- end
- if not retAll or #answers == 0 then
- stdnse.debug1("dns.answerFetcher found no records of the required type: AAAA")
- return false, "No Answers"
- end
- return true, answers
+ return generic_finder(dtype, dec, "answers", retAll)
end
---
@@ -777,20 +595,7 @@ end
-- @return True if one or more answers of the required type were found - otherwise false.
-- @return Answer according to the answer fetcher for dtype or an Error message.
function findNiceAdditional(dtype, dec, retAll)
- if (#dec.add > 0) then
- if additionalFetcher[dtype] then
- return additionalFetcher[dtype](dec, retAll)
- else
- stdnse.debug1("dns.findNiceAdditional() does not have an additionalFetcher for dtype %s",
- (type(dtype) == 'string' and dtype) or type(dtype) or "nil")
- return false, "Unable to handle response"
- end
- elseif (dec.flags.RC3 and dec.flags.RC4) then
- return false, "No Such Name"
- else
- stdnse.debug1("dns.findNiceAdditional() found zero answers in a response, but got an unexpected flags.replycode")
- return false, "No Answers"
- end
+ return generic_finder(dtype, dec, "add", retAll)
end
--