module(..., package.seeall) -- Load libraries require("modelfunctions") require("fs") require("format") require("posix") require("validator") -- Set variables local configfile = "/etc/conf.d/dnscache" local processname = "dnscache" local packagename = "dnscache" local baseurl = "/etc/dnscache/" local descr = { } -- ################################################################################ -- LOCAL FUNCTIONS local function validateconfig(config) local success = true if config.value.IPSEND and not validator.is_ipv4(config.value.IPSEND.value) then config.value.IPSEND.errtxt = "Invalid IP address" success = false end if config.value.CACHESIZE and not validator.is_integer(config.value.CACHESIZE.value) then config.value.CACHESIZE.errtxt = "Must be an integer" success = false end if config.value.IP and not validator.is_ipv4(config.value.IP.value) then config.value.IP.errtxt = "Invalid IP address" success = false end return success, config end local function validatedomain(domain) local success = false local domains = getDomains() domain.value.domain.errtxt = "Invalid domain" for i,name in ipairs(domains.value) do if name == domain.value.domain.value then domain.value.domain.errtxt = nil success = true break end end for i,name in ipairs(domain.value.iplist.value) do if not validator.is_ipv4(name) then domain.value.iplist.errtxt = "Invalid IP address" success = false break end end return success, domain end -- ################################################################################ -- PUBLIC FUNCTIONS function startstop_service(action) return modelfunctions.startstop_service(processname, action) end function getstatus() return modelfunctions.getstatus(processname, packagename, "DNS Cache Status") end function getconfig() local conf = format.parse_ini_file(fs.read_file(configfile) or "", "") or {} local output = {} output.IPSEND = cfe({ value = conf.IPSEND or "", label="IP address for requests", descr="Use 0.0.0.0 for default address" }) output.CACHESIZE = cfe({ value=conf.CACHESIZE or "0", label="Cache size" }) output.IP = cfe({ value=conf.IP or "", label="IP address to listen on" }) output.FORWARDONLY = cfe({ type="boolean", value=(conf.FORWARDONLY and conf.FORWARDONLY ~= ""), label="Forward only", descr="Servers are parent caches, not root servers" }) return cfe({ type="group", value=output, label="DNS Cache Config" }) end function setconfig(config) local success, config = validateconfig(config) if success then local file = fs.read_file(configfile) or "" file = format.update_ini_file(file,"","IPSEND",config.value.IPSEND.value) file = format.update_ini_file(file,"","CACHESIZE",config.value.CACHESIZE.value) file = format.update_ini_file(file,"","IP",config.value.IP.value) if config.value.FORWARDONLY.value then file = format.update_ini_file(file,"","FORWARDONLY","true") else file = format.update_ini_file(file,"","FORWARDONLY","") end fs.write_file(configfile, file) else config.errtxt = "Failed to set config" end return config end function getconfigfile() -- FIXME Validate return modelfunctions.getfiledetails(configfile) end function setconfigfile(filedetails) -- FIXME Validate return modelfunctions.setfiledetails(filedetails, {configfile}) end function getIPs() local ipdir = baseurl.."ip" local iplist = cfe({ type="list", value={}, label="IP prefixes to respond to" }) if fs.is_dir(ipdir) then for i,name in ipairs(posix.dir(ipdir)) do if not string.match(name, "^%.") then if (fs.is_file(ipdir.."/"..name)) then table.insert(iplist.value, name) end end end end return cfe({ type="group", value={iplist=iplist} }) end function setIPs(iplist) local reverseIPs = {} for i,name in ipairs(iplist.value.iplist.value) do -- check if a valid (or partial) ip if not validator.is_partial_ipv4(name) then iplist.value.iplist.errtxt = "Invalid IP address" iplist.errtxt = "Failed to set IP list" break end reverseIPs[name] = i end if not iplist.errtxt then local currentIPlist = getIPs() for i,name in ipairs(currentIPlist.value.iplist.value) do if reverseIPs[name] then reverseIPs[name] = nil else -- need to delete the file os.remove(baseurl.."ip/"..name) end end for name in pairs(reverseIPs) do -- need to create the file fs.create_file(baseurl.."ip/"..name) end end return iplist end function getDomains() local domaindir = baseurl.."servers" local domainlist = cfe({ type="list", value={}, label="DNS Server Domains" }) if fs.is_dir(domaindir) then for i,name in ipairs(posix.dir(domaindir)) do if not string.match(name, "^%.") then if (fs.is_file(domaindir.."/"..name)) then table.insert(domainlist.value, name) end end end end return domainlist end function getNewDomain() local domain = cfe({ label="Domain" }) return cfe({ type="group", value={domain=domain} }) end function setNewDomain(domain) if "" ~= string.gsub(domain.value.domain.value..".", "%w+%.", "") then domain.value.domain.errtxt = "Invalid domain" domain.errtxt = "Failed to create domain" elseif fs.is_file(baseurl.."servers/"..domain.value.domain.value) then domain.value.domain.errtxt = "Domain already exists" domain.errtxt = "Failed to create domain" else fs.create_file(baseurl.."servers/"..domain.value.domain.value) domain.descr = "Created domain" end return domain end function getDomain(getdomainname) local domain = cfe({ value=getdomainname, label="Domain", errtxt="Invalid domain" }) local iplist = cfe({ type="list", value={}, label="List of DNS servers" }) local domains = getDomains() for i,name in ipairs(domains.value) do if name == getdomainname then domain.errtxt = nil break end end if not domain.errtxt then local content = fs.read_file(baseurl.."servers/"..getdomainname) or "" for name in string.gmatch(content.."\n", "([^\n]+)\n") do table.insert(iplist.value, name) end end return cfe({ type="group", value={domain=domain, iplist=iplist} }) end function setDomain(domain) local success, domain = validatedomain(domain) if success then fs.write_file(baseurl.."servers/"..domain.value.domain.value, table.concat(domain.value.iplist.value, "\n") ) else domain.errtxt = "Failed to save domain" end return domain end function deleteDomain(domainname) local cmdresult = cfe({ value="Domain not deleted", label="Delete domain result", errtxt="Invalid domain" }) local domains = getDomains() if domainname == "@" then cmdresult.errtxt = "Cannot delete root domain" else for i,name in ipairs(domains.value) do if name == domainname then os.remove(baseurl.."servers/"..name) cmdresult.errtxt = nil cmdresult.value = "Domain deleted" break end end end return cmdresult end