diff --git a/nselib/dns.lua b/nselib/dns.lua
index e55342464..c6e6ae5c7 100644
--- a/nselib/dns.lua
+++ b/nselib/dns.lua
@@ -5,11 +5,11 @@
-- The most common interface to this module are the query and
-- reverse functions. query performs a DNS query,
-- and reverse prepares an ip address to have a reverse query
--- performed.
+-- performed.
--
-- query takes two options - a domain name to look up and an
--- optional table of options. For more information on the options table,
--- see the documentation for query.
+-- optional table of options. For more information on the options table,
+-- see the documentation for query.
--
-- Example usage:
--
@@ -43,26 +43,26 @@ get_servers = nmap.get_dns_servers
-- @name types
-- @class table
types = {
- A = 1,
- NS = 2,
- SOA = 6,
- CNAME = 5,
- PTR = 12,
- HINFO = 13,
- MX = 15,
- TXT = 16,
- AAAA = 28,
- SRV = 33,
- OPT = 41,
- SSHFP = 44,
- NSEC = 47,
- AXFR = 252,
- ANY = 255
+ A = 1,
+ NS = 2,
+ SOA = 6,
+ CNAME = 5,
+ PTR = 12,
+ HINFO = 13,
+ MX = 15,
+ TXT = 16,
+ AAAA = 28,
+ SRV = 33,
+ OPT = 41,
+ SSHFP = 44,
+ NSEC = 47,
+ AXFR = 252,
+ ANY = 255
}
CLASS = {
- IN = 1,
- ANY = 255
+ IN = 1,
+ ANY = 255
}
@@ -77,51 +77,51 @@ CLASS = {
-- @return Status (true or false).
-- @return Response (if status is true).
local function sendPackets(data, host, port, timeout, cnt, multiple)
- local socket = nmap.new_socket("udp")
- local responses = {}
+ local socket = nmap.new_socket("udp")
+ local responses = {}
- socket:set_timeout(timeout)
+ socket:set_timeout(timeout)
- if ( not(multiple) ) then
- socket:connect( host, port, "udp" )
- end
+ if ( not(multiple) ) then
+ socket:connect( host, port, "udp" )
+ end
- for i = 1, cnt do
- local status, err
+ for i = 1, cnt do
+ local status, err
- if ( multiple ) then
- status, err = socket:sendto(host, port, data)
- else
- status, err = socket:send(data)
- end
+ if ( multiple ) then
+ status, err = socket:sendto(host, port, data)
+ else
+ status, err = socket:send(data)
+ end
- if (not(status)) then return false, err end
+ if (not(status)) then return false, err end
- local response
+ local response
- if ( multiple ) then
- while(true) do
+ if ( multiple ) then
+ while(true) do
+ status, response = socket:receive()
+ if( not(status) ) then break end
+
+ local status, _, _, ip, _ = socket:get_info()
+ table.insert(responses, { data = response, peer = ip } )
+ end
+ else
status, response = socket:receive()
- if( not(status) ) then break end
+ if ( status ) then
+ local status, _, _, ip, _ = socket:get_info()
+ table.insert(responses, { data = response, peer = ip } )
+ end
+ end
- local status, _, _, ip, _ = socket:get_info()
- table.insert(responses, { data = response, peer = ip } )
- end
- else
- status, response = socket:receive()
- if ( status ) then
- local status, _, _, ip, _ = socket:get_info()
- table.insert(responses, { data = response, peer = ip } )
- end
- end
-
- if (#responses>0) then
- socket:close()
- return true, responses
- end
- end
- socket:close()
- return false
+ if (#responses>0) then
+ socket:close()
+ return true, responses
+ end
+ end
+ socket:close()
+ return false
end
@@ -130,35 +130,35 @@ end
-- @param rPkt Decoded DNS response packet.
-- @return True if useful, false if not.
local function gotAnswer(rPkt)
- -- have we even got answers?
- if #rPkt.answers > 0 then
+ -- have we even got answers?
+ if #rPkt.answers > 0 then
- -- some MDNS implementation incorrectly return an empty question section
- -- if this is the case return true
- if rPkt.questions[1] == nil then
- return true
- end
+ -- some MDNS implementation incorrectly return an empty question section
+ -- if this is the case return true
+ if rPkt.questions[1] == nil then
+ return true
+ end
- -- are those answers not just cnames?
- if rPkt.questions[1].dtype == types.A then
- for _, v in ipairs(rPkt.answers) do
- -- if at least one answer is an A record, it's an answer
- if v.dtype == types.A then
- return true
+ -- are those answers not just cnames?
+ if rPkt.questions[1].dtype == types.A then
+ for _, v in ipairs(rPkt.answers) do
+ -- if at least one answer is an A record, it's an answer
+ if v.dtype == types.A then
+ return true
+ end
end
- end
- -- if none was an A record, it's not really an answer
- return false
- else -- there was no A request, CNAMEs are not of interest
- return true
- end
- -- no such name is the answer
- elseif rPkt.flags.RC3 and rPkt.flags.RC4 then
- return true
- -- really no answer
- else
- return false
- end
+ -- if none was an A record, it's not really an answer
+ return false
+ else -- there was no A request, CNAMEs are not of interest
+ return true
+ end
+ -- no such name is the answer
+ elseif rPkt.flags.RC3 and rPkt.flags.RC4 then
+ return true
+ -- really no answer
+ else
+ return false
+ end
end
@@ -168,65 +168,65 @@ end
-- @param rPkt Decoded DNS response packet
-- @return String or table of next server(s) to query, or false.
local function getAuthDns(rPkt)
- if #rPkt.auth == 0 then
- if #rPkt.answers == 0 then
- return false
- else
- if rPkt.answers[1].dtype == types.CNAME then
- return {cname = rPkt.answers[1].domain}
- end
- end
- end
- if rPkt.auth[1].dtype == types.NS then
- if #rPkt.add > 0 then
- local hosts = {}
- for _, v in ipairs(rPkt.add) do
- if v.dtype == types.A then
- table.insert(hosts, v.ip)
+ if #rPkt.auth == 0 then
+ if #rPkt.answers == 0 then
+ return false
+ else
+ if rPkt.answers[1].dtype == types.CNAME then
+ return {cname = rPkt.answers[1].domain}
end
- end
- if #hosts > 0 then return hosts end
- end
- local status, next = query(rPkt.auth[1].domain, {dtype = "A" })
- return next
- end
- return false
+ end
+ end
+ if rPkt.auth[1].dtype == types.NS then
+ if #rPkt.add > 0 then
+ local hosts = {}
+ for _, v in ipairs(rPkt.add) do
+ if v.dtype == types.A then
+ table.insert(hosts, v.ip)
+ end
+ end
+ if #hosts > 0 then return hosts end
+ end
+ local status, next = query(rPkt.auth[1].domain, {dtype = "A" })
+ return next
+ end
+ return false
end
local function processResponse( response, dname, dtype, options )
- local rPkt = decode(response)
- -- is it a real answer?
- if gotAnswer(rPkt) then
- if (options.retPkt) then
- return true, rPkt
- else
- return findNiceAnswer(dtype, rPkt, options.retAll)
- end
- else -- if not, ask the next server in authority
+ local rPkt = decode(response)
+ -- is it a real answer?
+ if gotAnswer(rPkt) then
+ if (options.retPkt) then
+ return true, rPkt
+ else
+ return findNiceAnswer(dtype, rPkt, options.retAll)
+ end
+ else -- if not, ask the next server in authority
- local next_server = getAuthDns(rPkt)
-
- -- if we got a CNAME, ask for the CNAME
- if type(next_server) == 'table' and next_server.cname then
- options.tries = options.tries - 1
- return query(next_server.cname, options)
- end
+ local next_server = getAuthDns(rPkt)
+
+ -- if we got a CNAME, ask for the CNAME
+ if type(next_server) == 'table' and next_server.cname then
+ options.tries = options.tries - 1
+ return query(next_server.cname, options)
+ end
+
+ -- only ask next server in authority, if
+ -- we got an auth dns and
+ -- it isn't the one we just asked
+ if next_server and next_server ~= options.host and options.tries > 1 then
+ options.host = next_server
+ options.tries = options.tries - 1
+ return query(dname, options)
+ end
+ end
+
+ -- nothing worked
+ stdnse.print_debug(1, "dns.query() failed to resolve the requested query%s%s", dname and ": " or ".", dname or "")
+ return false, "No Answers"
- -- only ask next server in authority, if
- -- we got an auth dns and
- -- it isn't the one we just asked
- if next_server and next_server ~= options.host and options.tries > 1 then
- options.host = next_server
- options.tries = options.tries - 1
- return query(dname, options)
- end
- end
-
- -- nothing worked
- stdnse.print_debug(1, "dns.query() failed to resolve the requested query%s%s", dname and ": " or ".", dname or "")
- return false, "No Answers"
-
end
---
@@ -246,74 +246,74 @@ end
-- @return String answer of the requested type, table of answers or a String error message of one of the following:
-- "No Such Name", "No Servers", "No Answers", "Unable to handle response"
function query(dname, options)
- if not options then options = {} end
+ if not options then options = {} end
- local dtype, host, port = options.dtype, options.host, options.port
+ local dtype, host, port = options.dtype, options.host, options.port
- if not options.tries then options.tries = 10 end -- don't get into an infinite loop
+ if not options.tries then options.tries = 10 end -- don't get into an infinite loop
- if not options.sendCount then options.sendCount = 2 end
-
- if type( options.timeout ) ~= "number" then options.timeout = get_default_timeout() end
+ if not options.sendCount then options.sendCount = 2 end
- if type(dtype) == "string" then
- dtype = types[dtype]
- end
- if not dtype then dtype = types.A end
+ if type( options.timeout ) ~= "number" then options.timeout = get_default_timeout() end
- local srv
- local srvI = 1
- if not port then port = 53 end
- if not host then
- srv = get_servers()
- if srv and srv[1] then
- host = srv[1]
- else
- return false, "No Servers"
- end
- elseif type(host) == "table" then
- srv = host
- host = srv[1]
- end
-
- local pkt = newPacket()
- addQuestion(pkt, dname, dtype)
- if options.norecurse then pkt.flags.RD = false end
-
- if options.dnssec then
- addOPT(pkt, {DO = true})
- end
-
- local data = encode(pkt)
-
- local status, response = sendPackets(data, host, port, options.timeout, options.sendCount, options.multiple)
-
-
- -- if working with know nameservers, try the others
- while((not status) and srv and srvI < #srv) do
- srvI = srvI + 1
- host = srv[srvI]
- status, response = sendPackets(data, host, port, options.timeout, options.sendCount)
- end
-
- -- if we got any response:
- if status then
- if ( options.multiple ) then
- local multiresponse = {}
- for _, r in ipairs( response ) do
- local status, presponse = processResponse( r.data, dname, dtype, options )
- if( status ) then
- table.insert( multiresponse, { ['output']=presponse, ['peer']=r.peer } )
- end
- end
- return true, multiresponse
- else
- return processResponse( response[1].data, dname, dtype, options)
+ if type(dtype) == "string" then
+ dtype = types[dtype]
+ end
+ if not dtype then dtype = types.A end
+
+ local srv
+ local srvI = 1
+ if not port then port = 53 end
+ if not host then
+ srv = get_servers()
+ if srv and srv[1] then
+ host = srv[1]
+ else
+ return false, "No Servers"
+ end
+ elseif type(host) == "table" then
+ srv = host
+ host = srv[1]
+ end
+
+ local pkt = newPacket()
+ addQuestion(pkt, dname, dtype)
+ if options.norecurse then pkt.flags.RD = false end
+
+ if options.dnssec then
+ addOPT(pkt, {DO = true})
+ end
+
+ local data = encode(pkt)
+
+ local status, response = sendPackets(data, host, port, options.timeout, options.sendCount, options.multiple)
+
+
+ -- if working with know nameservers, try the others
+ while((not status) and srv and srvI < #srv) do
+ srvI = srvI + 1
+ host = srv[srvI]
+ status, response = sendPackets(data, host, port, options.timeout, options.sendCount)
+ end
+
+ -- if we got any response:
+ if status then
+ if ( options.multiple ) then
+ local multiresponse = {}
+ for _, r in ipairs( response ) do
+ local status, presponse = processResponse( r.data, dname, dtype, options )
+ if( status ) then
+ table.insert( multiresponse, { ['output']=presponse, ['peer']=r.peer } )
+ end
+ end
+ return true, multiresponse
+ else
+ return processResponse( response[1].data, dname, dtype, options)
+ end
+ else
+ stdnse.print_debug(1, "dns.query() got zero responses attempting to resolve query%s%s", dname and ": " or ".", dname or "")
+ return false, "No Answers"
end
- else
- stdnse.print_debug(1, "dns.query() got zero responses attempting to resolve query%s%s", dname and ": " or ".", dname or "")
- return false, "No Answers"
- end
end
@@ -323,35 +323,35 @@ end
-- @return "Domain"-style representation of IP as subdomain of in-addr.arpa or
-- ip6.arpa.
function reverse(ip)
- ip = ipOps.expand_ip(ip)
- if type(ip) ~= "string" then return nil end
- local delim = "%."
- local arpa = ".in-addr.arpa"
- if ip:match(":") then
- delim = ":"
- arpa = ".ip6.arpa"
- end
- local ipParts = stdnse.strsplit(delim, ip)
- if #ipParts == 8 then
- -- padding
- local mask = "0000"
- for i, part in ipairs(ipParts) do
- ipParts[i] = mask:sub(1, string.len(mask) - string.len(part)) .. part
- end
- -- 32 parts from 8
- local temp = {}
- for i, hdt in ipairs(ipParts) do
- for part in hdt:gmatch("%x") do
- temp[#temp+1] = part
- end
- end
- ipParts = temp
- end
- local ipReverse = {}
- for i = #ipParts, 1, -1 do
- table.insert(ipReverse, ipParts[i])
- end
- return table.concat(ipReverse, ".") .. arpa
+ ip = ipOps.expand_ip(ip)
+ if type(ip) ~= "string" then return nil end
+ local delim = "%."
+ local arpa = ".in-addr.arpa"
+ if ip:match(":") then
+ delim = ":"
+ arpa = ".ip6.arpa"
+ end
+ local ipParts = stdnse.strsplit(delim, ip)
+ if #ipParts == 8 then
+ -- padding
+ local mask = "0000"
+ for i, part in ipairs(ipParts) do
+ ipParts[i] = mask:sub(1, string.len(mask) - string.len(part)) .. part
+ end
+ -- 32 parts from 8
+ local temp = {}
+ for i, hdt in ipairs(ipParts) do
+ for part in hdt:gmatch("%x") do
+ temp[#temp+1] = part
+ end
+ end
+ ipParts = temp
+ end
+ local ipReverse = {}
+ for i = #ipParts, 1, -1 do
+ table.insert(ipReverse, ipParts[i])
+ end
+ return table.concat(ipReverse, ".") .. arpa
end
-- Table for answer fetching functions.
@@ -363,26 +363,26 @@ local answerFetcher = {}
-- @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.
answerFetcher[types.TXT] = function(dec, retAll)
- local answers = {}
- if not retAll and dec.answers[1].data then
- return string.sub(dec.answers[1].data, 2)
- elseif not retAll then
- stdnse.print_debug(1, "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)
+ local answers = {}
+ if not retAll and dec.answers[1].data then
+ return string.sub(dec.answers[1].data, 2)
+ elseif not retAll then
+ stdnse.print_debug(1, "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)
+ end
end
- end
- end
- end
- if #answers == 0 then
- stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: TXT")
- return false, "No Answers"
- end
- return true, answers
+ end
+ end
+ if #answers == 0 then
+ stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: TXT")
+ return false, "No Answers"
+ end
+ return true, answers
end
-- Answer fetcher for A records
@@ -391,20 +391,20 @@ end
-- @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.print_debug(1, "dns.answerFetcher found no records of the required type: A")
- return false, "No Answers"
- end
- return true, answers
+ 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.print_debug(1, "dns.answerFetcher found no records of the required type: A")
+ return false, "No Answers"
+ end
+ return true, answers
end
@@ -414,22 +414,22 @@ end
-- @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.print_debug(1, "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.print_debug(1, "dns.answerFetcher found no records of the required type: NS, PTR or CNAME")
- return false, "No Answers"
- end
- return true, answers
+ local answers = {}
+ if not retAll and dec.answers[1].domain then
+ return true, dec.answers[1].domain
+ elseif not retAll then
+ stdnse.print_debug(1, "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.print_debug(1, "dns.answerFetcher found no records of the required type: NS, PTR or CNAME")
+ return false, "No Answers"
+ end
+ return true, answers
end
-- Answer fetcher for MX records.
@@ -440,30 +440,30 @@ end
-- 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.print_debug(1, "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
+ 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.print_debug(1, "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
-- Answer fetcher for SRV records.
@@ -476,18 +476,18 @@ end
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
+ 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.print_debug(1, "dns.answerFetcher found no records of the required type: SRV")
- return false, "No Answers"
+ stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: SRV")
+ return false, "No Answers"
end
-
+
return true, answers
end
@@ -498,20 +498,20 @@ end
-- @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.print_debug(1, "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 "-", stdnse.strjoin(":", nsecrec.types) or "-"))
- end
- if not retAll then return true, answers[1] end
- return true, answers
+ 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.print_debug(1, "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 "-", stdnse.strjoin(":", nsecrec.types) or "-"))
+ end
+ if not retAll then return true, answers[1] end
+ return true, answers
end
-- Answer fetcher for NS records.
@@ -537,20 +537,20 @@ answerFetcher[types.PTR] = answerFetcher[types.CNAME]
-- @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)
- end
- end
- if not retAll or #answers == 0 then
- stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: AAAA")
- return false, "No Answers"
- end
- return true, answers
+ 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)
+ end
+ end
+ if not retAll or #answers == 0 then
+ stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: AAAA")
+ return false, "No Answers"
+ end
+ return true, answers
end
@@ -562,20 +562,20 @@ 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 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.print_debug(1, "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.print_debug(1, "dns.findNiceAnswer() found zero answers in a response, but got an unexpected flags.replycode")
- return false, "No Answers"
- end
+function findNiceAnswer(dtype, dec, retAll)
+ if (#dec.answers > 0) then
+ if answerFetcher[dtype] then
+ return answerFetcher[dtype](dec, retAll)
+ else
+ stdnse.print_debug(1, "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.print_debug(1, "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.
@@ -592,26 +592,26 @@ local additionalFetcher = {}
-- @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 string.sub(dec.add[1].data, 2)
- elseif not retAll then
- stdnse.print_debug(1, "dns.aditionalFetcher 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)
+ local answers = {}
+ if not retAll and dec.add[1].data then
+ return string.sub(dec.add[1].data, 2)
+ elseif not retAll then
+ stdnse.print_debug(1, "dns.aditionalFetcher 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
- end
- if #answers == 0 then
- stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: TXT")
- return false, "No Answers"
- end
- return true, answers
+ end
+ end
+ if #answers == 0 then
+ stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: TXT")
+ return false, "No Answers"
+ end
+ return true, answers
end
-- Additional fetcher for A records
@@ -620,20 +620,20 @@ end
-- @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.print_debug(1, "dns.answerFetcher found no records of the required type: A")
- return false, "No Answers"
- end
- return true, answers
+ 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.print_debug(1, "dns.answerFetcher found no records of the required type: A")
+ return false, "No Answers"
+ end
+ return true, answers
end
@@ -647,16 +647,16 @@ end
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
+ 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.print_debug(1, "dns.answerFetcher found no records of the required type: SRV")
- return false, "No Answers"
+ stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: SRV")
+ return false, "No Answers"
end
return true, answers
@@ -669,20 +669,20 @@ end
-- @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.print_debug(1, "dns.answerFetcher found no records of the required type: AAAA")
- return false, "No Answers"
- end
- return true, answers
+ 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.print_debug(1, "dns.answerFetcher found no records of the required type: AAAA")
+ return false, "No Answers"
+ end
+ return true, answers
end
---
@@ -693,21 +693,21 @@ 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 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.print_debug(1, "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.print_debug(1, "dns.findNiceAdditional() found zero answers in a response, but got an unexpected flags.replycode")
- return false, "No Answers"
- end
+function findNiceAdditional(dtype, dec, retAll)
+ if (#dec.add > 0) then
+ if additionalFetcher[dtype] then
+ return additionalFetcher[dtype](dec, retAll)
+ else
+ stdnse.print_debug(1, "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.print_debug(1, "dns.findNiceAdditional() found zero answers in a response, but got an unexpected flags.replycode")
+ return false, "No Answers"
+ end
end
--
@@ -715,15 +715,15 @@ end
-- @param fqdn containing the fully qualified domain name
-- @return encQ containing the encoded value
local function encodeFQDN(fqdn)
- if ( not(fqdn) or #fqdn == 0 ) then return end
-
- local parts = stdnse.strsplit("%.", fqdn)
- local encQ = ""
- for _, part in ipairs(parts) do
- encQ = encQ .. bin.pack("p", part)
- end
- encQ = encQ .. string.char(0)
- return encQ
+ if ( not(fqdn) or #fqdn == 0 ) then return end
+
+ local parts = stdnse.strsplit("%.", fqdn)
+ local encQ = ""
+ for _, part in ipairs(parts) do
+ encQ = encQ .. bin.pack("p", part)
+ end
+ encQ = encQ .. string.char(0)
+ return encQ
end
---
@@ -731,13 +731,13 @@ end
-- @param questions Table of questions.
-- @return Encoded question string.
local function encodeQuestions(questions)
- if type(questions) ~= "table" then return nil end
- local encQ = ""
- for _, v in ipairs(questions) do
- encQ = encQ .. encodeFQDN(v.dname)
- encQ = encQ .. bin.pack(">SS", v.dtype, v.class)
- end
- return encQ
+ if type(questions) ~= "table" then return nil end
+ local encQ = ""
+ for _, v in ipairs(questions) do
+ encQ = encQ .. encodeFQDN(v.dname)
+ encQ = encQ .. bin.pack(">SS", v.dtype, v.class)
+ end
+ return encQ
end
---
@@ -745,17 +745,17 @@ end
-- @param questions Table of questions.
-- @return Encoded question string.
local function encodeZones(zones)
- return encodeQuestions(zones)
+ return encodeQuestions(zones)
end
local function encodeUpdates(updates)
- if type(updates) ~= "table" then return nil end
- local encQ = ""
- for _, v in ipairs(updates) do
- encQ = encQ .. encodeFQDN(v.dname)
- encQ = encQ .. bin.pack(">SSISA", v.dtype, v.class, v.ttl, #v.data, v.data)
- end
- return encQ
+ if type(updates) ~= "table" then return nil end
+ local encQ = ""
+ for _, v in ipairs(updates) do
+ encQ = encQ .. encodeFQDN(v.dname)
+ encQ = encQ .. bin.pack(">SSISA", v.dtype, v.class, v.ttl, #v.data, v.data)
+ end
+ return encQ
end
---
@@ -765,12 +765,12 @@ end
-- and rdata.
-- @return Encoded additional string.
local function encodeAdditional(additional)
- if type(additional) ~= "table" then return nil end
- local encA = ""
- for _, v in ipairs(additional) do
- encA = encA .. bin.pack(">xSSISA", v.type, v.class, v.ttl, v.rdlen, v.rdata)
- end
- return encA
+ if type(additional) ~= "table" then return nil end
+ local encA = ""
+ for _, v in ipairs(additional) do
+ encA = encA .. bin.pack(">xSSISA", v.type, v.class, v.ttl, v.rdlen, v.rdata)
+ end
+ return encA
end
---
@@ -779,24 +779,24 @@ end
-- RA, RCx).
-- @return Binary digit string representing flags.
local function encodeFlags(flags)
- if type(flags) == "string" then return flags end
- if type(flags) ~= "table" then return nil end
- local fb = ""
- if flags.QR then fb = fb .. "1" else fb = fb .. "0" end
- if flags.OC1 then fb = fb .. "1" else fb = fb .. "0" end
- if flags.OC2 then fb = fb .. "1" else fb = fb .. "0" end
- if flags.OC3 then fb = fb .. "1" else fb = fb .. "0" end
- if flags.OC4 then fb = fb .. "1" else fb = fb .. "0" end
- if flags.AA then fb = fb .. "1" else fb = fb .. "0" end
- if flags.TC then fb = fb .. "1" else fb = fb .. "0" end
- if flags.RD then fb = fb .. "1" else fb = fb .. "0" end
- if flags.RA then fb = fb .. "1" else fb = fb .. "0" end
- fb = fb .. "000"
- if flags.RC1 then fb = fb .. "1" else fb = fb .. "0" end
- if flags.RC2 then fb = fb .. "1" else fb = fb .. "0" end
- if flags.RC3 then fb = fb .. "1" else fb = fb .. "0" end
- if flags.RC4 then fb = fb .. "1" else fb = fb .. "0" end
- return fb
+ if type(flags) == "string" then return flags end
+ if type(flags) ~= "table" then return nil end
+ local fb = ""
+ if flags.QR then fb = fb .. "1" else fb = fb .. "0" end
+ if flags.OC1 then fb = fb .. "1" else fb = fb .. "0" end
+ if flags.OC2 then fb = fb .. "1" else fb = fb .. "0" end
+ if flags.OC3 then fb = fb .. "1" else fb = fb .. "0" end
+ if flags.OC4 then fb = fb .. "1" else fb = fb .. "0" end
+ if flags.AA then fb = fb .. "1" else fb = fb .. "0" end
+ if flags.TC then fb = fb .. "1" else fb = fb .. "0" end
+ if flags.RD then fb = fb .. "1" else fb = fb .. "0" end
+ if flags.RA then fb = fb .. "1" else fb = fb .. "0" end
+ fb = fb .. "000"
+ if flags.RC1 then fb = fb .. "1" else fb = fb .. "0" end
+ if flags.RC2 then fb = fb .. "1" else fb = fb .. "0" end
+ if flags.RC3 then fb = fb .. "1" else fb = fb .. "0" end
+ if flags.RC4 then fb = fb .. "1" else fb = fb .. "0" end
+ return fb
end
---
@@ -807,25 +807,25 @@ end
-- newPacket.
-- @return Encoded DNS packet.
function encode(pkt)
- if type(pkt) ~= "table" then return nil end
- local encFlags = encodeFlags(pkt.flags)
- local questions = encodeQuestions(pkt.questions)
- local additional = encodeAdditional(pkt.additional)
- local qorzlen = #pkt.questions
- local aorplen = #pkt.answers
- local aorulen = #pkt.auth
+ if type(pkt) ~= "table" then return nil end
+ local encFlags = encodeFlags(pkt.flags)
+ local questions = encodeQuestions(pkt.questions)
+ local additional = encodeAdditional(pkt.additional)
+ local qorzlen = #pkt.questions
+ local aorplen = #pkt.answers
+ local aorulen = #pkt.auth
- if ( #pkt.questions < 1 ) then
- -- The packet has no questions, assume we're dealing with an update
- data = encodeZones( pkt.zones )
- qorzlen = #pkt.zones
+ if ( #pkt.questions < 1 ) then
+ -- The packet has no questions, assume we're dealing with an update
+ data = encodeZones( pkt.zones )
+ qorzlen = #pkt.zones
- aorulen = #pkt.updates
- data = data .. encodeUpdates( pkt.updates )
- end
+ aorulen = #pkt.updates
+ data = data .. encodeUpdates( pkt.updates )
+ end
- local encStr = bin.pack(">SBS4", pkt.id, encFlags, qorzlen, aorplen, aorulen, #pkt.additional) .. questions .. additional
- return encStr
+ local encStr = bin.pack(">SBS4", pkt.id, encFlags, qorzlen, aorplen, aorulen, #pkt.additional) .. questions .. additional
+ return encStr
end
@@ -836,40 +836,40 @@ end
-- @return Position after decoding.
-- @return Decoded domain, or nil on error.
function decStr(data, pos)
- local function dec(data, pos, limit)
- local partlen
- local parts = {}
- local part
+ local function dec(data, pos, limit)
+ local partlen
+ local parts = {}
+ local part
- -- Avoid infinite recursion on malformed compressed messages.
- limit = limit or 10
- if limit < 0 then
- return pos, nil
- end
+ -- Avoid infinite recursion on malformed compressed messages.
+ limit = limit or 10
+ if limit < 0 then
+ return pos, nil
+ end
- pos, partlen = bin.unpack(">C", data, pos)
- while (partlen ~= 0) do
- if (partlen < 64) then
- pos, part = bin.unpack("A" .. partlen, data, pos)
- if part == nil then
- return pos
+ pos, partlen = bin.unpack(">C", data, pos)
+ while (partlen ~= 0) do
+ if (partlen < 64) then
+ pos, part = bin.unpack("A" .. partlen, data, pos)
+ if part == nil then
+ return pos
+ end
+ table.insert(parts, part)
+ pos, partlen = bin.unpack(">C", data, pos)
+ else
+ pos, partlen = bin.unpack(">S", data, pos - 1)
+ local _, part = dec(data, partlen - 0xC000 + 1, limit - 1)
+ if part == nil then
+ return pos
+ end
+ table.insert(parts, part)
+ partlen = 0
end
- table.insert(parts, part)
- pos, partlen = bin.unpack(">C", data, pos)
- else
- pos, partlen = bin.unpack(">S", data, pos - 1)
- local _, part = dec(data, partlen - 0xC000 + 1, limit - 1)
- if part == nil then
- return pos
- end
- table.insert(parts, part)
- partlen = 0
- end
- end
- return pos, table.concat(parts, ".")
- end
+ end
+ return pos, table.concat(parts, ".")
+ end
- return dec(data, pos)
+ return dec(data, pos)
end
@@ -881,14 +881,14 @@ end
-- @return Position after decoding.
-- @return Table of decoded questions.
local function decodeQuestions(data, count, pos)
- local q = {}
- for i = 1, count do
- local currQ = {}
- pos, currQ.dname = decStr(data, pos)
- pos, currQ.dtype, currQ.class = bin.unpack(">SS", data, pos)
- table.insert(q, currQ)
- end
- return pos, q
+ local q = {}
+ for i = 1, count do
+ local currQ = {}
+ pos, currQ.dname = decStr(data, pos)
+ pos, currQ.dtype, currQ.class = bin.unpack(">SS", data, pos)
+ table.insert(q, currQ)
+ end
+ return pos, q
end
@@ -899,23 +899,23 @@ local decoder = {}
-- Decodes IP of A record, puts it in entry.ip.
-- @param entry RR in packet.
decoder[types.A] = function(entry)
- local ip = {}
- local _
- _, ip[1], ip[2], ip[3], ip[4] = bin.unpack(">C4", entry.data)
- entry.ip = table.concat(ip, ".")
+ local ip = {}
+ local _
+ _, ip[1], ip[2], ip[3], ip[4] = bin.unpack(">C4", entry.data)
+ entry.ip = table.concat(ip, ".")
end
-- Decodes IP of AAAA record, puts it in entry.ipv6.
-- @param entry RR in packet.
decoder[types.AAAA] = function(entry)
- local ip = {}
- local pos = 1
- local num
- for i = 1, 8 do
- pos, num = bin.unpack(">S", entry.data, pos)
- table.insert(ip, string.format('%x', num))
- end
- entry.ipv6 = table.concat(ip, ":")
+ local ip = {}
+ local pos = 1
+ local num
+ for i = 1, 8 do
+ pos, num = bin.unpack(">S", entry.data, pos)
+ table.insert(ip, string.format('%x', num))
+ end
+ entry.ipv6 = table.concat(ip, ":")
end
-- Decodes SSH fingerprint record, puts it in entry.SSHFP as
@@ -925,10 +925,10 @@ end
-- fptype, and fingerprint.
-- @param entry RR in packet.
decoder[types.SSHFP] = function(entry)
- local _
- entry.SSHFP = {}
- _, entry.SSHFP.algorithm,
- entry.SSHFP.fptype, entry.SSHFP.fingerprint = bin.unpack(">C2H" .. (#entry.data - 2), entry.data)
+ local _
+ entry.SSHFP = {}
+ _, entry.SSHFP.algorithm,
+ entry.SSHFP.fptype, entry.SSHFP.fingerprint = bin.unpack(">C2H" .. (#entry.data - 2), entry.data)
end
@@ -942,38 +942,38 @@ end
-- @param pos Position in packet after RR.
decoder[types.SOA] = function(entry, data, pos)
- local np = pos - #entry.data
+ local np = pos - #entry.data
- entry.SOA = {}
+ entry.SOA = {}
- np, entry.SOA.mname = decStr(data, np)
- np, entry.SOA.rname = decStr(data, np)
- np, entry.SOA.serial,
- entry.SOA.refresh,
- entry.SOA.retry,
- entry.SOA.expire,
- entry.SOA.minimum
- = bin.unpack(">I5", data, np)
+ np, entry.SOA.mname = decStr(data, np)
+ np, entry.SOA.rname = decStr(data, np)
+ np, entry.SOA.serial,
+ entry.SOA.refresh,
+ entry.SOA.retry,
+ entry.SOA.expire,
+ entry.SOA.minimum
+ = bin.unpack(">I5", data, np)
end
-- 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
+ 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)
+ 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,
@@ -985,18 +985,18 @@ end
-- @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
- entry.NSEC = {}
- entry.NSEC.dname = entry.dname
- np, entry.NSEC.next_dname = decStr(data, np)
- while np < pos do
- local block_num, type_bitmap
- np, block_num, type_bitmap = bin.unpack(">Cp", data, np)
- entry.NSEC.types = {}
- for i in bit_iter(type_bitmap) do
- entry.NSEC.types[(block_num - 1) * 256 + i] = true
- end
- end
+ local np = pos - #entry.data
+ entry.NSEC = {}
+ entry.NSEC.dname = entry.dname
+ np, entry.NSEC.next_dname = decStr(data, np)
+ while np < pos do
+ local block_num, type_bitmap
+ np, block_num, type_bitmap = bin.unpack(">Cp", data, np)
+ entry.NSEC.types = {}
+ for i in bit_iter(type_bitmap) do
+ entry.NSEC.types[(block_num - 1) * 256 + i] = true
+ end
+ end
end
-- Decodes records that consist only of one domain, for example CNAME, NS, PTR.
@@ -1005,10 +1005,10 @@ end
-- @param data Complete encoded DNS packet.
-- @param pos Position in packet after RR.
local function decDomain(entry, data, pos)
- local np = pos - #entry.data
- local _
- _, entry.domain = decStr(data, np)
- end
+ local np = pos - #entry.data
+ local _
+ _, entry.domain = decStr(data, np)
+ end
-- Decodes CNAME records.
-- Puts result in entry.domain.
@@ -1044,25 +1044,25 @@ decoder[types.PTR] = decDomain
-- @param entry RR in packet.
-- @param data Complete encoded DNS packet.
-- @param pos Position in packet after RR.
-decoder[types.TXT] =
- function (entry, data, pos)
-
- local len = entry.data:len()
- local np = pos - #entry.data
- local txt_len
- local txt
+decoder[types.TXT] =
+ function (entry, data, pos)
- if len > 0 then
- entry.TXT = {}
- entry.TXT.text = {}
- end
+ local len = entry.data:len()
+ local np = pos - #entry.data
+ local txt_len
+ local txt
- while len > 0 do
- np, txt_len = bin.unpack("C", data, np)
- np, txt = bin.unpack("A" .. txt_len, data, np )
- len = len - txt_len - 1
- table.insert( entry.TXT.text, txt )
- end
+ if len > 0 then
+ entry.TXT = {}
+ entry.TXT.text = {}
+ end
+
+ while len > 0 do
+ np, txt_len = bin.unpack("C", data, np)
+ np, txt = bin.unpack("A" .. txt_len, data, np )
+ len = len - txt_len - 1
+ table.insert( entry.TXT.text, txt )
+ end
end
@@ -1073,30 +1073,30 @@ decoder[types.TXT] =
-- @param entry RR in packet.
-- @param data Complete encoded DNS packet.
-- @param pos Position in packet after RR.
-decoder[types.MX] =
- function(entry, data, pos)
- local np = pos - #entry.data + 2
- local _
- entry.MX = {}
- _, entry.MX.pref = bin.unpack(">S", entry.data)
- _, entry.MX.server = decStr(data, np)
- end
+decoder[types.MX] =
+ function(entry, data, pos)
+ local np = pos - #entry.data + 2
+ local _
+ entry.MX = {}
+ _, entry.MX.pref = bin.unpack(">S", entry.data)
+ _, entry.MX.server = decStr(data, np)
+ end
-- Decodes SRV record, puts it in entry.SRV.
--
-- entry.SRV has the fields prio,
--- weight, port and
+-- weight, port and
-- target.
-- @param entry RR in packet.
-- @param data Complete encoded DNS packet.
-- @param pos Position in packet after RR.
decoder[types.SRV] =
function(entry, data, pos)
- local np = pos - #entry.data
- local _
- entry.SRV = {}
- np, entry.SRV.prio, entry.SRV.weight, entry.SRV.port = bin.unpack(">S>S>S", data, np)
- np, entry.SRV.target = decStr(data, np)
+ local np = pos - #entry.data
+ local _
+ entry.SRV = {}
+ np, entry.SRV.prio, entry.SRV.weight, entry.SRV.port = bin.unpack(">S>S>S", data, np)
+ np, entry.SRV.target = decStr(data, np)
end
---
@@ -1106,25 +1106,25 @@ decoder[types.SRV] =
-- @param pos Starting position in packet.
-- @return Table of RRs.
local function decodeRR(data, count, pos)
- local ans = {}
- for i = 1, count do
- local currRR = {}
- pos, currRR.dname = decStr(data, pos)
- pos, currRR.dtype, currRR.class, currRR.ttl = bin.unpack(">SSI", data, pos)
+ local ans = {}
+ for i = 1, count do
+ local currRR = {}
+ pos, currRR.dname = decStr(data, pos)
+ pos, currRR.dtype, currRR.class, currRR.ttl = bin.unpack(">SSI", data, pos)
- local reslen
- pos, reslen = bin.unpack(">S", data, pos)
+ local reslen
+ pos, reslen = bin.unpack(">S", data, pos)
- pos, currRR.data = bin.unpack("A" .. reslen, data, pos)
+ pos, currRR.data = bin.unpack("A" .. reslen, data, pos)
- -- try to be smart: decode per type
- if decoder[currRR.dtype] then
- decoder[currRR.dtype](currRR, data, pos)
- end
+ -- try to be smart: decode per type
+ if decoder[currRR.dtype] then
+ decoder[currRR.dtype](currRR, data, pos)
+ end
- table.insert(ans, currRR)
- end
- return pos, ans
+ table.insert(ans, currRR)
+ end
+ return pos, ans
end
---
@@ -1132,11 +1132,11 @@ end
-- @param str String to be split up.
-- @return Table of characters.
local function str2tbl(str)
- local tbl = {}
- for i = 1, #str do
- table.insert(tbl, string.sub(str, i, i))
- end
- return tbl
+ local tbl = {}
+ for i = 1, #str do
+ table.insert(tbl, string.sub(str, i, i))
+ end
+ return tbl
end
---
@@ -1144,22 +1144,22 @@ end
-- @param flgStr Flags as a binary digit string.
-- @return Table representing flags.
local function decodeFlags(flgStr)
- local flags = {}
- local flgTbl = str2tbl(flgStr)
- if flgTbl[1] == '1' then flags.QR = true end
- if flgTbl[2] == '1' then flags.OC1 = true end
- if flgTbl[3] == '1' then flags.OC2 = true end
- if flgTbl[4] == '1' then flags.OC3 = true end
- if flgTbl[5] == '1' then flags.OC4 = true end
- if flgTbl[6] == '1' then flags.AA = true end
- if flgTbl[7] == '1' then flags.TC = true end
- if flgTbl[8] == '1' then flags.RD = true end
- if flgTbl[9] == '1' then flags.RA = true end
- if flgTbl[13] == '1' then flags.RC1 = true end
- if flgTbl[14] == '1' then flags.RC2 = true end
- if flgTbl[15] == '1' then flags.RC3 = true end
- if flgTbl[16] == '1' then flags.RC4 = true end
- return flags
+ local flags = {}
+ local flgTbl = str2tbl(flgStr)
+ if flgTbl[1] == '1' then flags.QR = true end
+ if flgTbl[2] == '1' then flags.OC1 = true end
+ if flgTbl[3] == '1' then flags.OC2 = true end
+ if flgTbl[4] == '1' then flags.OC3 = true end
+ if flgTbl[5] == '1' then flags.OC4 = true end
+ if flgTbl[6] == '1' then flags.AA = true end
+ if flgTbl[7] == '1' then flags.TC = true end
+ if flgTbl[8] == '1' then flags.RD = true end
+ if flgTbl[9] == '1' then flags.RA = true end
+ if flgTbl[13] == '1' then flags.RC1 = true end
+ if flgTbl[14] == '1' then flags.RC2 = true end
+ if flgTbl[15] == '1' then flags.RC3 = true end
+ if flgTbl[16] == '1' then flags.RC4 = true end
+ return flags
end
---
@@ -1167,29 +1167,29 @@ end
-- @param data Encoded DNS packet.
-- @return Table representing DNS packet.
function decode(data)
- local pos
- local pkt = {}
- local encFlags
- local cnt = {}
- pos, pkt.id, encFlags, cnt.q, cnt.a, cnt.auth, cnt.add = bin.unpack(">SB2S4", data)
- -- for now, don't decode the flags
- pkt.flags = decodeFlags(encFlags)
+ local pos
+ local pkt = {}
+ local encFlags
+ local cnt = {}
+ pos, pkt.id, encFlags, cnt.q, cnt.a, cnt.auth, cnt.add = bin.unpack(">SB2S4", data)
+ -- for now, don't decode the flags
+ pkt.flags = decodeFlags(encFlags)
- --
- -- check whether this is an update response or not
- -- a quick fix to allow decoding of non updates and not break for updates
- -- the flags are enough for the current code to determine whether an update was successful or not
- --
- local strflags=encodeFlags(pkt.flags)
- if ( strflags:sub(1,4) == "1010" ) then
- return pkt
- else
- pos, pkt.questions = decodeQuestions(data, cnt.q, pos)
- pos, pkt.answers = decodeRR(data, cnt.a, pos)
- pos, pkt.auth = decodeRR(data, cnt.auth, pos)
- pos, pkt.add = decodeRR(data, cnt.add, pos)
- end
- return pkt
+ --
+ -- check whether this is an update response or not
+ -- a quick fix to allow decoding of non updates and not break for updates
+ -- the flags are enough for the current code to determine whether an update was successful or not
+ --
+ local strflags=encodeFlags(pkt.flags)
+ if ( strflags:sub(1,4) == "1010" ) then
+ return pkt
+ else
+ pos, pkt.questions = decodeQuestions(data, cnt.q, pos)
+ pos, pkt.answers = decodeRR(data, cnt.a, pos)
+ pos, pkt.auth = decodeRR(data, cnt.auth, pos)
+ pos, pkt.add = decodeRR(data, cnt.add, pos)
+ end
+ return pkt
end
@@ -1197,17 +1197,17 @@ end
-- Creates a new table representing a DNS packet.
-- @return Table representing a DNS packet.
function newPacket()
- local pkt = {}
- pkt.id = 1
- pkt.flags = {}
- pkt.flags.RD = true
- pkt.questions = {}
- pkt.zones = {}
- pkt.updates = {}
- pkt.answers = {}
- pkt.auth = {}
- pkt.additional = {}
- return pkt
+ local pkt = {}
+ pkt.id = 1
+ pkt.flags = {}
+ pkt.flags.RD = true
+ pkt.questions = {}
+ pkt.zones = {}
+ pkt.updates = {}
+ pkt.answers = {}
+ pkt.auth = {}
+ pkt.additional = {}
+ return pkt
end
@@ -1217,14 +1217,14 @@ end
-- @param dname Domain name to be asked.
-- @param dtype RR to be asked.
function addQuestion(pkt, dname, dtype)
- if type(pkt) ~= "table" then return nil end
- if type(pkt.questions) ~= "table" then return nil end
- local q = {}
- q.dname = dname
- q.dtype = dtype
- q.class = CLASS.IN
- table.insert(pkt.questions, q)
- return pkt
+ if type(pkt) ~= "table" then return nil end
+ if type(pkt.questions) ~= "table" then return nil end
+ local q = {}
+ q.dname = dname
+ q.dtype = dtype
+ q.class = CLASS.IN
+ table.insert(pkt.questions, q)
+ return pkt
end
@@ -1233,14 +1233,14 @@ get_default_timeout = function()
return timeout[nmap.timing_level()] or 4000
end
----
+---
-- Adds a zone to a DNS packet table
-- @param pkt Table representing DNS packet.
-- @param dname Domain name to be asked.
function addZone(pkt, dname)
- if ( type(pkt) ~= "table" ) or (type(pkt.updates) ~= "table") then return nil end
- table.insert(pkt.zones, { dname=dname, dtype=types.SOA, class=CLASS.IN })
- return pkt
+ if ( type(pkt) ~= "table" ) or (type(pkt.updates) ~= "table") then return nil end
+ table.insert(pkt.zones, { dname=dname, dtype=types.SOA, class=CLASS.IN })
+ return pkt
end
---
@@ -1248,15 +1248,15 @@ end
-- @param flags Flag table, each entry representing a flag (only DO flag implmented).
-- @return Binary digit string representing flags.
local function encodeOPT_Z(flags)
- if type(flags) == "string" then return flags end
- if type(flags) ~= "table" then return nil end
- local bits = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
- for n, k in pairs({[1] = "DO"}) do
- if flags[k] then
- bits[n] = 1
- end
- end
- return table.concat(bits)
+ if type(flags) == "string" then return flags end
+ if type(flags) ~= "table" then return nil end
+ local bits = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+ for n, k in pairs({[1] = "DO"}) do
+ if flags[k] then
+ bits[n] = 1
+ end
+ end
+ return table.concat(bits)
end
---
@@ -1265,21 +1265,21 @@ end
-- @param pkt Table representing DNS packet.
-- @param Z Table of Z flags. Only DO is supported.
function addOPT(pkt, Z)
- if type(pkt) ~= "table" then return nil end
- if type(pkt.additional) ~= "table" then return nil end
- local _, Z_int = bin.unpack(">S", bin.pack("B", encodeOPT_Z(Z)))
- local opt = {
- type = types.OPT,
- class = 4096, -- Actually the sender UDP payload size.
- ttl = 0 * (0x01000000) + 0 * (0x00010000) + Z_int,
- rdlen = 0,
- rdata = "",
- }
- table.insert(pkt.additional, opt)
- return pkt
+ if type(pkt) ~= "table" then return nil end
+ if type(pkt.additional) ~= "table" then return nil end
+ local _, Z_int = bin.unpack(">S", bin.pack("B", encodeOPT_Z(Z)))
+ local opt = {
+ type = types.OPT,
+ class = 4096, -- Actually the sender UDP payload size.
+ ttl = 0 * (0x01000000) + 0 * (0x00010000) + Z_int,
+ rdlen = 0,
+ rdata = "",
+ }
+ table.insert(pkt.additional, opt)
+ return pkt
end
----
+---
-- Adds a update to a DNS packet table
-- @param pkt Table representing DNS packet.
-- @param dname Domain name to be asked.
@@ -1287,9 +1287,9 @@ end
-- @param ttl the time-to-live of the record
-- @param data type specific data
function addUpdate(pkt, dname, dtype, ttl, data, class)
- if ( type(pkt) ~= "table" ) or (type(pkt.updates) ~= "table") then return nil end
- table.insert(pkt.updates, { dname=dname, dtype=dtype, class=class, ttl=ttl, data=(data or "") } )
- return pkt
+ if ( type(pkt) ~= "table" ) or (type(pkt.updates) ~= "table") then return nil end
+ table.insert(pkt.updates, { dname=dname, dtype=dtype, class=class, ttl=ttl, data=(data or "") } )
+ return pkt
end
@@ -1302,10 +1302,10 @@ end
-- * sendCount: The number of send attempts to perform
-- * zone: If not supplied deduced from hostname
-- * data: Table or string containing update data (depending on record type):
--- A - String containing the IP address
--- CNAME - String containing the FQDN
--- MX - Table containing pref, mx
--- SRV - Table containing prio, weight, port, target
+-- A - String containing the IP address
+-- CNAME - String containing the FQDN
+-- MX - Table containing pref, mx
+-- SRV - Table containing prio, weight, port, target
--
-- @return status true on success false on failure
-- @return msg containing the error message
@@ -1314,86 +1314,84 @@ end
--
-- Adding different types of records to a server
-- * update( "www.cqure.net", { host=host, port=port, dtype="A", data="10.10.10.10" } )
--- * update( "alias.cqure.net", { host=host, port=port, dtype="CNAME", data="www.cqure.net" } )
--- * update( "cqure.net", { host=host, port=port, dtype="MX", data={ pref=10, mx="mail.cqure.net"} })
--- * update( "_ldap._tcp.cqure.net", { host=host, port=port, dtype="SRV", data={ prio=0, weight=100, port=389, target="ldap.cqure.net" } } )
+-- * update( "alias.cqure.net", { host=host, port=port, dtype="CNAME", data="www.cqure.net" } )
+-- * update( "cqure.net", { host=host, port=port, dtype="MX", data={ pref=10, mx="mail.cqure.net"} })
+-- * update( "_ldap._tcp.cqure.net", { host=host, port=port, dtype="SRV", data={ prio=0, weight=100, port=389, target="ldap.cqure.net" } } )
--
-- Removing the above records by setting an empty data and a ttl of zero
--- * update( "www.cqure.net", { host=host, port=port, dtype="A", data="", ttl=0 } )
--- * update( "alias.cqure.net", { host=host, port=port, dtype="CNAME", data="", ttl=0 } )
--- * update( "cqure.net", { host=host, port=port, dtype="MX", data="", ttl=0 } )
--- * update( "_ldap._tcp.cqure.net", { host=host, port=port, dtype="SRV", data="", ttl=0 } )
+-- * update( "www.cqure.net", { host=host, port=port, dtype="A", data="", ttl=0 } )
+-- * update( "alias.cqure.net", { host=host, port=port, dtype="CNAME", data="", ttl=0 } )
+-- * update( "cqure.net", { host=host, port=port, dtype="MX", data="", ttl=0 } )
+-- * update( "_ldap._tcp.cqure.net", { host=host, port=port, dtype="SRV", data="", ttl=0 } )
--
function update(dname, options)
- local options = options or {}
- local pkt = newPacket()
- local flags = pkt.flags
- local host, port = options.host, options.port
- local timeout = ( type(options.timeout) == "number" ) and options.timeout or get_default_timeout()
- local sendcount = options.sendCount or 2
- local dtype = ( type(options.dtype) == "string" ) and types[options.dtype] or types.A
- local updata = options.data
- local ttl = options.ttl or 86400
- local zone = options.zone or dname:match("^.-%.(.+)$")
- local class = CLASS.IN
+ local options = options or {}
+ local pkt = newPacket()
+ local flags = pkt.flags
+ local host, port = options.host, options.port
+ local timeout = ( type(options.timeout) == "number" ) and options.timeout or get_default_timeout()
+ local sendcount = options.sendCount or 2
+ local dtype = ( type(options.dtype) == "string" ) and types[options.dtype] or types.A
+ local updata = options.data
+ local ttl = options.ttl or 86400
+ local zone = options.zone or dname:match("^.-%.(.+)$")
+ local class = CLASS.IN
- assert(host, "dns.update needs a valid host in options")
- assert(port, "dns.update needs a valid port in options")
-
- if ( options.zone ) then dname = dname .. "." .. options.zone end
+ assert(host, "dns.update needs a valid host in options")
+ assert(port, "dns.update needs a valid port in options")
- if ( not(zone) and not( dname:match("^.-%..+") ) ) then
- return false, "hostname needs to be supplied as FQDN"
- end
-
- flags.RD = false
- flags.OC1, flags.OC2, flags.OC3, flags.OC4 = false, true, false, true
-
- -- If ttl is zero and updata is string and zero length or nil, assume delete record
- if ( ttl == 0 and ( ( type(updata) == "string" and #updata == 0 ) or not(updata) ) ) then
- class = CLASS.ANY
- updata = ""
- if ( types.MX == dtype and not(options.zone) ) then zone=dname end
- if ( types.SRV == dtype and not(options.zone) ) then
- zone=dname:match("^_.-%._.-%.(.+)$")
- end
- -- if not, let's try to update the zone
- else
- if ( dtype == types.A ) then
- updata = updata and bin.pack(">I", ipOps.todword(updata)) or ""
- elseif( dtype == types.CNAME ) then
- updata = encodeFQDN(updata)
- elseif( dtype == types.MX ) then
- assert( not( type(updata) ~= "table" ), "dns.update expected options.data to be a table")
- if ( not(options.zone) ) then zone = dname end
- local data = bin.pack(">S", updata.pref)
- data = data .. encodeFQDN(updata.mx)
- updata = data
- elseif ( dtype == types.SRV ) then
- assert( not( type(updata) ~= "table" ), "dns.update expected options.data to be a table")
- local data = bin.pack(">SSS", updata.prio, updata.weight, updata.port )
- data = data .. encodeFQDN(updata.target)
- updata = data
- zone = options.zone or dname:match("^_.-%._.-%.(.+)$")
- else
- return false, "Unsupported record type"
- end
- end
-
- pkt = addZone(pkt, zone)
- pkt = addUpdate(pkt, dname, dtype, ttl, updata, class)
-
- local data = encode(pkt)
- local status, response = sendPackets(data, host, port, timeout, sendcount, false)
-
- if ( status ) then
- local decoded = decode(response[1].data)
- local flags=encodeFlags(decoded.flags)
- if (flags:sub(-4) == "0000") then
- return true
- end
- end
- return false
+ if ( options.zone ) then dname = dname .. "." .. options.zone end
+
+ if ( not(zone) and not( dname:match("^.-%..+") ) ) then
+ return false, "hostname needs to be supplied as FQDN"
+ end
+
+ flags.RD = false
+ flags.OC1, flags.OC2, flags.OC3, flags.OC4 = false, true, false, true
+
+ -- If ttl is zero and updata is string and zero length or nil, assume delete record
+ if ( ttl == 0 and ( ( type(updata) == "string" and #updata == 0 ) or not(updata) ) ) then
+ class = CLASS.ANY
+ updata = ""
+ if ( types.MX == dtype and not(options.zone) ) then zone=dname end
+ if ( types.SRV == dtype and not(options.zone) ) then
+ zone=dname:match("^_.-%._.-%.(.+)$")
+ end
+ -- if not, let's try to update the zone
+ else
+ if ( dtype == types.A ) then
+ updata = updata and bin.pack(">I", ipOps.todword(updata)) or ""
+ elseif( dtype == types.CNAME ) then
+ updata = encodeFQDN(updata)
+ elseif( dtype == types.MX ) then
+ assert( not( type(updata) ~= "table" ), "dns.update expected options.data to be a table")
+ if ( not(options.zone) ) then zone = dname end
+ local data = bin.pack(">S", updata.pref)
+ data = data .. encodeFQDN(updata.mx)
+ updata = data
+ elseif ( dtype == types.SRV ) then
+ assert( not( type(updata) ~= "table" ), "dns.update expected options.data to be a table")
+ local data = bin.pack(">SSS", updata.prio, updata.weight, updata.port )
+ data = data .. encodeFQDN(updata.target)
+ updata = data
+ zone = options.zone or dname:match("^_.-%._.-%.(.+)$")
+ else
+ return false, "Unsupported record type"
+ end
+ end
+
+ pkt = addZone(pkt, zone)
+ pkt = addUpdate(pkt, dname, dtype, ttl, updata, class)
+
+ local data = encode(pkt)
+ local status, response = sendPackets(data, host, port, timeout, sendcount, false)
+
+ if ( status ) then
+ local decoded = decode(response[1].data)
+ local flags=encodeFlags(decoded.flags)
+ if (flags:sub(-4) == "0000") then
+ return true
+ end
+ end
+ return false
end
-
-