From baa4453186d1dcceb1461f385acbb8c119c7b6fe Mon Sep 17 00:00:00 2001 From: Filip Znachor Date: Sat, 24 Jul 2021 20:41:55 +0200 Subject: [PATCH] Added DynDNS --- DynDNS/README.md | 20 +++++ DynDNS/dyndns.lua | 212 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 DynDNS/README.md create mode 100644 DynDNS/dyndns.lua diff --git a/DynDNS/README.md b/DynDNS/README.md new file mode 100644 index 0000000..68b216c --- /dev/null +++ b/DynDNS/README.md @@ -0,0 +1,20 @@ +# DynDNS + +Dynamic DNS records can be used for assigning hostnames to local devices. + +It combines **DHCPv4 server leases** with **IP neigh** and add ALL device addresses to DNS hostnames. + +Can be used for assinging **RA addresses to device hostnames**, which are defaultly **NOT** assigned in OpenWrt. + +Add this script to **dnsmasq's dhcp-script** option: +``` +dhcp-script = lua /path/to/dyndns.lua +``` + +Or via dhcpscript in uci: +``` +config dnsmasq + option dhcpscript 'lua /path/to/dyndns.lua' +``` + +Also add `list addnhosts '/tmp/dyndns'` to make dnsmasq using DynDNS's hosts file. \ No newline at end of file diff --git a/DynDNS/dyndns.lua b/DynDNS/dyndns.lua new file mode 100644 index 0000000..c4ffc6f --- /dev/null +++ b/DynDNS/dyndns.lua @@ -0,0 +1,212 @@ +-- Configuration + +local lookup_dhcp_leases = true +local dhcp_leases_file = "/tmp/dhcp.leases" + +local lookup_ip_neigh = true + +local non_fqdn = true +local network_domain = "chata.lan" + +local output_file = "/tmp/dyndns" + +local reload_command = "/etc/init.d/dnsmasq reload" + + +function split(inputstr, sep) + if sep == nil then + sep = "%s" + end + local t={} + for str in string.gmatch(inputstr, "([^"..sep.."]+)") do + table.insert(t, str) + end + return t +end + +function new() + return {hostname = "?", ip = {}} +end + +function contains(tab, val) + for index, value in ipairs(tab) do + if value == val then + return true + end + end + + return false +end + +function ipv4(ip) + local chunks = {ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")} + if (#chunks == 4) then + for _,v in pairs(chunks) do + if (tonumber(v) < 0 or tonumber(v) > 255) then + return false + end + end + return true + else + return false + end +end + +function ipv6(ip) + local _, chunks = ip:gsub("[%a%d]+%:?", "") + if chunks == 8 then + return true + end + return false +end + +function count(T) + local count = 0 + for _ in pairs(T) do count = count + 1 end + return count +end + +function print_table(node) + local cache, stack, output = {},{},{} + local depth = 1 + local output_str = "{\n" + + while true do + local size = 0 + for k,v in pairs(node) do + size = size + 1 + end + + local cur_index = 1 + for k,v in pairs(node) do + if (cache[node] == nil) or (cur_index >= cache[node]) then + + if (string.find(output_str,"}",output_str:len())) then + output_str = output_str .. ",\n" + elseif not (string.find(output_str,"\n",output_str:len())) then + output_str = output_str .. "\n" + end + + -- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings + table.insert(output,output_str) + output_str = "" + + local key + if (type(k) == "number" or type(k) == "boolean") then + key = "["..tostring(k).."]" + else + key = "['"..tostring(k).."']" + end + + if (type(v) == "number" or type(v) == "boolean") then + output_str = output_str .. string.rep('\t',depth) .. key .. " = "..tostring(v) + elseif (type(v) == "table") then + output_str = output_str .. string.rep('\t',depth) .. key .. " = {\n" + table.insert(stack,node) + table.insert(stack,v) + cache[node] = cur_index+1 + break + else + output_str = output_str .. string.rep('\t',depth) .. key .. " = '"..tostring(v).."'" + end + + if (cur_index == size) then + output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. "}" + else + output_str = output_str .. "," + end + else + -- close the table + if (cur_index == size) then + output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. "}" + end + end + + cur_index = cur_index + 1 + end + + if (size == 0) then + output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. "}" + end + + if (#stack > 0) then + node = stack[#stack] + stack[#stack] = nil + depth = cache[node] == nil and depth + 1 or depth - 1 + else + break + end + end + + -- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings + table.insert(output,output_str) + output_str = table.concat(output) + + print(output_str) +end + + + +array = {} + +if lookup_dhcp_leases then + + local dhcp_file = assert(io.popen('cat ' .. dhcp_leases_file, 'r')) + local dhcp_table = split(dhcp_file:read('*all'), "\r\n") + dhcp_file:close() + + for key,value in pairs(dhcp_table) do + local dhcp_lease = split(value, " ") + local mac = dhcp_lease[2] + if not array[mac] then + array[mac] = new() + array[mac]["hostname"] = dhcp_lease[4] + end + if not contains(array[mac]["ip"], dhcp_lease[3]) then + table.insert(array[mac]["ip"], dhcp_lease[3]) + end + end + +end + +if lookup_ip_neigh then + + local neigh_file = assert(io.popen('ip neigh', 'r')) + local neigh_table = split(neigh_file:read('*all'), "\r\n") + neigh_file:close() + + for key,value in pairs(neigh_table) do + local neigh = split(value, " ") + local mac = neigh[5] + local ip = neigh[1] + if mac and ip then + if not array[mac] then + array[mac] = new() + end + if not contains(array[mac]["ip"], ip) then + table.insert(array[mac]["ip"], ip) + end + end + end + +end + +config = "" + +for key,value in pairs(array) do + local mac = key + local ip = value["ip"] + local hostname = value["hostname"] + if hostname ~= "?" then + for key,value in pairs(ip) do + if non_fqdn then config = config .. "\r\n" .. value .. " " .. hostname end + if network_domain then config = config .. "\r\n" .. value .. " " .. hostname .. "." .. network_domain end + end + end +end + +file = io.open(output_file, "w") +file:write(config) +file:close() + +os.execute(reload_command) \ No newline at end of file