-
Notifications
You must be signed in to change notification settings - Fork 911
Lua Examples (Recursor)
Warning - these are all outdated and will probably not work with 4.x
Please consult the documentation at https://doc.powerdns.com/recursor/lua-scripting/index.html before deploying any of those Lua Scripts and be warned that should you do any blocking operations in these Scripts your queries will also block indefinitely and thus any scripting errors may render your recursive nameservice broken.
Always test your Lua script before deploying it to your live recursors.
Additional examples are part of the PowerDNS sources:
- https://github.com/PowerDNS/pdns/blob/master/pdns/recursordist/contrib/powerdns-example-script.lua
- https://github.com/PowerDNS/pdns/blob/master/pdns/recursordist/contrib/dns64.lua
Feel free to add your own Snipplets - this is a wiki! ;)
Wraps your nameservers local time in a TXT record.
function preresolve ( remoteip, domain, qtype )
if domain == "the.time."
then
d=os.date("\"%c\"")
ret={
{qtype=pdns.TXT, ttl=1, place="1", content=d},
}
if qtype == pdns.TXT
then
return 0, ret
else
return -1, {}
end
end
return -1, {}
end
function nxdomain ( remoteip, domain, qtype )
return -1, {}
end
Note: this should probably be rewritten as a postresolve script after the release of Recursor 3.4.
This will return an A record if a query for IN A or ANY www.powerdns.org would otherwise give an NXDOMAIN response.
ranges={
"127.0.0.0/24", -- localnet for debugging
}
hostnames={
"nothere%.example%.com", -- domain we want to be redirect if it triggers an NXDOMAIN response
}
function preresolve ( remoteip, domain, qtype )
return -1, {}
end
function nxdomain ( remoteip, domain, qtype )
if matchnetmask(remoteip, ranges) -- this is a c function that the recursor exports to the Lua script
then
domain=string.lower(domain)
for i,v in pairs(hostnames)
do
if string.find(domain, "^" .. v .. "%.$")
then
if qtype == pdns.A or qtype == pdns.ANY
then
return 0, {
{qtype=pdns.A, content="1.2.3.4", ttl=3600, place="1"},
}
else
return 0, {}
end
end
end
end
return -1, {}
end
Turns your Recursor into a poor man's STUN server.
function preresolve (...)
ip = arg[1]
if #arg == 3 then
domain = arg[2]
qtype = arg[3]
else
destination = arg[2]
domain = arg[3]
qtype = arg[4]
end
if qtype == pdns.TXT and string.lower( domain ) == "whatsmyip." then
setvariable()
return 0, {{qtype=qtype, ttl=10, place="1", content='"'..ip..'"'}}
end
return -1, {}
end
function nxdomain (...)
ip = arg[1]
if #arg == 3 then
domain = arg[2]
qtype = arg[3]
else
destination = arg[2]
domain = arg[3]
qtype = arg[4]
end
return -1, {}
end
Note: this should probably be rewritten as a postresolve script after the release of Recursor 3.4.
Here we use "preresolve" to return a private IP address in the 192.168.x.x range for a host when inside a LAN. For example, the DynDNS-managed hostname "mymachine.homelinux.org" should yield the public IP address of the LAN gateway when queried on the Internet (where the query goes to the DynDNS nameservers), but 192.168.0.10 when queried on the LAN (where the query should go to locally configured pdns-recursor). For fun and Lua training, we also translate the numeric query type to its cleartext representation when logging.
-- Provide a function to translate the query type to to a cleartext string.
-- If the query type is unknown, its numeric value is returned as string.
-- The query types have been copy/pasted from pdns/qtype.hh with regex replacements
-- and sorting.
do
local qtt = {
[1] = "A",
[38] = "A6",
[28] = "AAAA",
[65400] = "ADDR",
[18] = "AFSDB",
[65401] = "ALIAS",
[255] = "ANY",
[252] = "AXFR",
[257] = "CAA",
[60] = "CDNSKEY",
[59] = "CDS",
[37] = "CERT",
[5] = "CNAME",
[49] = "DHCID",
[32769] = "DLV",
[39] = "DNAME",
[48] = "DNSKEY",
[43] = "DS",
[108] = "EUI48",
[109] = "EUI64",
[13] = "HINFO",
[45] = "IPSECKEY",
[251] = "IXFR",
[25] = "KEY",
[36] = "KX",
[29] = "LOC",
[254] = "MAILA",
[253] = "MAILB",
[14] = "MINFO",
[9] = "MR",
[15] = "MX",
[35] = "NAPTR",
[2] = "NS",
[47] = "NSEC",
[50] = "NSEC3",
[51] = "NSEC3PARAM",
[61] = "OPENPGPKEY",
[41] = "OPT",
[12] = "PTR",
[57] = "RKEY",
[17] = "RP",
[46] = "RRSIG",
[24] = "SIG",
[6] = "SOA",
[99] = "SPF",
[33] = "SRV",
[44] = "SSHFP",
[249] = "TKEY",
[52] = "TLSA",
[250] = "TSIG",
[16] = "TXT",
[256] = "URI",
[11] = "WKS" }
function translateQtype ( qtype )
local str = qtt[qtype]
if str then
return str
else
return tostring(qtype)
end
end
-- example: print ( translateQtype( 161 ) ) --> "161"
-- print ( translateQtype( 249 ) ) --> "TKEY"
end
-- "preresolve" is called by pdns-resolver.
-- The parameter list is the old style one (prior to version. 3.1.7)
-- for newer versions, use preresolve ( requestorip, acceptorip, domain, qtype )
-- Look in the system logfile for the logging text.
function preresolve ( requestorip, domain, qtype )
pdnslog ("preresolve() called by " .. tostring(requestorip) .. " for domain=" .. tostring(domain) .. ", type=" .. translateQtype(qtype) )
if domain == "mymachine.homelinux.org." and qtype == pdns.A
then
return 0, { {qtype=pdns.A, content="192.168.0.10"} }
else
return -1, {}
end
end
Sometimes, we need to create a custom dns response for a Non-Existent Domain
domainame = "test.example.org"
response = "192.168.1.10"
function nxdomain(dq)
if dq.qname:equal(domainame) then
dq.rcode=0 -- make it a normal answer
dq:addAnswer(pdns.A, response)
dq.variable = true -- disable packet cache
return true
end
return false
end
Please also read the PowerDNS Documentation that is available from https://doc.powerdns.com/