-
-
Save stintel/0ea70c262649b8b122961e9b23ea951d to your computer and use it in GitHub Desktop.
PowerDNS DNSdist: forward/reverse DNS lookup from ISC Kea DHCP leases
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package.path = package.path .. ";" .. "/path/to/dir/containing/this/script/" .. "?.lua" | |
require "kea" | |
local dhcpDomains = newSuffixMatchNode() | |
dhcpDomains:add("8.b.d.0.1.0.0.2.ip6.arpa") | |
dhcpDomains:add("0.2.0.192.in-addr.arpa.") | |
dhcpDomains:add("example.net") | |
local nmg = newNMG() | |
nmg:addMask("192.0.2.0/24") | |
nmg:addMask("2001:0bd8::/32") | |
addAction(AndRule({NetmaskGroupRule(nmg), SuffixMatchNodeRule(dhcpDomains)}), LuaAction(dhcpLookup)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- without this dnsdist cannot find some modules | |
package.path = package.path .. ";" .. "/usr/lib/lua/" .. "?.lua" | |
local cjson = require("cjson") | |
local http = require("socket.http") | |
local ltn12 = require("ltn12") | |
local keaCtrlAgentUrl = "http://localhost:8000" | |
local dhcpPostLease4GetAll = { | |
command = "lease4-get-all", | |
service = {"dhcp4"} | |
} | |
local dhcpPostLease6GetAll = { | |
command = "lease6-get-all", | |
service = {"dhcp6"} | |
} | |
local function keaCtrlAgentPost(data) | |
local jsonData = cjson.encode(data) | |
local response = {} | |
local res, code, headers, status = http.request { | |
headers = { | |
["Content-Type"] = "application/json", | |
-- Kea control agent does not support chunked transfer encoding | |
-- set the Content-Length header to disable it | |
["Content-Length"] = string.len(jsonData), | |
}, | |
method = "POST", | |
sink = ltn12.sink.table(response), | |
source = ltn12.source.string(jsonData), | |
url = keaCtrlAgentUrl, | |
} | |
-- print("keaCtrlAgentPost: " .. jsonData) | |
if code == 200 then | |
response = table.concat(response) | |
return cjson.decode(response) | |
else | |
return {} | |
end | |
end | |
local function compressip6(addr) | |
local result = table.concat(addr, ":") | |
local max_start, max_zeros, zero_count, zero_start = 0, 0, 0, 0 | |
for i, group in ipairs(addr) do | |
if group == "0" then | |
if zero_start == 0 then | |
zero_start = i | |
end | |
zero_count = zero_count + 1 | |
else | |
if zero_count > max_zeros then | |
max_zeros = zero_count | |
max_start = zero_start | |
end | |
zero_start = 0 | |
zero_count = 0 | |
end | |
end | |
if zero_count > max_zeros then | |
max_zeros = zero_count | |
max_start = zero_start | |
end | |
if max_zeros > 1 then | |
local after, before = "", "" | |
if max_start + max_zeros <= #addr then | |
after = table.concat(addr, ":", max_start + max_zeros) | |
end | |
if max_start > 1 then | |
before = table.concat(addr, ":", 1, max_start - 1) | |
end | |
result = before .. "::" .. after | |
end | |
return result | |
end | |
local function reverse4(record) | |
local result = {} | |
for octet in record:gmatch("%d+") do | |
table.insert(result, octet) | |
end | |
return result[4] .. "." .. result[3] .. "." .. result[2] .. "." .. result[1] | |
end | |
local function reverse6(record) | |
local result = {} | |
record = string.gsub(record, "%.ip6.arpa%.*$", "") | |
record = string.gsub(record, "%.", "") | |
record = string.reverse(record) | |
for word in record:gmatch("%x%x%x%x") do | |
table.insert(result, string.format("%0x", tonumber(word, 16))) | |
end | |
result = compressip6(result) | |
return result | |
end | |
local function lookupForward(leases, hostname) | |
for _, entry in pairs(leases) do | |
for _, lease in pairs(entry.arguments.leases) do | |
-- print(lease.hostname) | |
if lease.hostname == hostname or lease.hostname .. "." == hostname then | |
return lease["ip-address"] | |
end | |
end | |
end | |
return nil | |
end | |
local function lookupReverse(leases, ip) | |
-- print("lookupReverse(): ip: " .. ip) | |
for _, entry in pairs(leases) do | |
for _, lease in pairs(entry.arguments.leases) do | |
-- print(lease.hostname) | |
if lease["ip-address"] == ip then | |
return lease["hostname"] | |
end | |
end | |
end | |
return nil | |
end | |
function dhcpLookup(dq) | |
if dq == nil then | |
-- print("dhcpLookup() called with nil argument") | |
return | |
end | |
local qname = dq.qname:toString() | |
local response = nil | |
-- print("dhcpLookup(): " .. qname) | |
if dq.qtype == DNSQType.A then | |
-- print("dhcpLookup() A " .. qname) | |
local dhcpLeases4 = keaCtrlAgentPost(dhcpPostLease4GetAll) | |
response = lookupForward(dhcpLeases4, qname) | |
elseif dq.qtype == DNSQType.AAAA then | |
-- print("dhcpLookup() AAAA " .. qname) | |
local dhcpLeases6 = keaCtrlAgentPost(dhcpPostLease6GetAll) | |
response = lookupForward(dhcpLeases6, qname) | |
elseif dq.qtype == DNSQType.PTR then | |
-- print("dhcpLookup() PTR " .. qname) | |
if string.match(qname, "in-addr.arpa.") ~= nil then | |
-- print("dhcpLook() PTR IPv4") | |
local dhcpLeases4 = keaCtrlAgentPost(dhcpPostLease4GetAll) | |
response = lookupReverse(dhcpLeases4, reverse4(qname)) | |
elseif qname:gmatch("ip6.arpa.") then | |
-- print("dhcpLook() PTR IPv6") | |
local dhcpLeases6 = keaCtrlAgentPost(dhcpPostLease6GetAll) | |
response = lookupReverse(dhcpLeases6, reverse6(qname)) | |
else | |
-- print("dhcpLook() PTR invalid") | |
end | |
else | |
-- print("not A nor AAAA but " .. dq.qtype) | |
return DNSAction.None | |
end | |
if response == nil then | |
return DNSAction.Nxdomain | |
else | |
return DNSAction.Spoof, response | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment