summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--dnsmasq-config-html.lsp31
-rw-r--r--dnsmasq-model.lua332
3 files changed, 195 insertions, 170 deletions
diff --git a/Makefile b/Makefile
index a431593..b510894 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
APP_NAME=dnsmasq
PACKAGE=acf-$(APP_NAME)
-VERSION=0.1.1
+VERSION=0.1.0
APP_DIST=\
dnsmasq* \
diff --git a/dnsmasq-config-html.lsp b/dnsmasq-config-html.lsp
index 9272f40..cb0060a 100644
--- a/dnsmasq-config-html.lsp
+++ b/dnsmasq-config-html.lsp
@@ -18,7 +18,36 @@ end %>
<H1>Config</H1>
<%
form.action = page_info.script .. page_info.prefix .. page_info.controller .. "/" .. page_info.action
- displayform(form)
+ displayformstart(form)
+%>
+<H2>General Parameters</H2>
+<dl>
+<%
+ displayformitem(form.value.interface, "interface")
+ displayformitem(form.value.listen_address, "listen_address")
+ displayformitem(form.value.domain, "domain")
+%>
+</dl>
+
+<H2>DNS Parameters</H2>
+<dl>
+<%
+ displayformitem(form.value.mx_host, "mx_host")
+%>
+</dl>
+
+<H2>DHCP Parameters</H2>
+<dl>
+<%
+ displayformitem(form.value.dhcp_range, "dhcp_range")
+ displayformitem(form.value.no_dhcp_interface, "no_dhcp_interface")
+ displayformitem(form.value.dhcp_host, "dhcp_host")
+ displayformitem(form.value.dhcp_option, "dhcp_option")
+%>
+</dl>
+
+<%
+ displayformend(form)
%>
<% if viewlibrary and viewlibrary.dispatch_component then
diff --git a/dnsmasq-model.lua b/dnsmasq-model.lua
index cacb3d8..dbd600a 100644
--- a/dnsmasq-model.lua
+++ b/dnsmasq-model.lua
@@ -4,52 +4,169 @@ module(..., package.seeall)
require("modelfunctions")
require("fs")
require("format")
-require("posix")
require("validator")
-- Set variables
local configfile = "/etc/dnsmasq.conf"
local processname = "dnsmasq"
local packagename = "dnsmasq"
-local baseurl = "/etc/"
-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
+local function update_file (file, search_name, value_in)
+ if not file or not search_name or search_name == "" then
+ return file, 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
+
+ -- Since we clear out the value, this prevents us from changing parent's value
+ local value = value_in
+ if type(value_in) == "table" then
+ value = {}
+ for i,val in ipairs(value_in) do
+ value[#value + 1] = val
+ end
end
- return success, config
+
+ local new_conf_file = {}
+ local skip_lines = {}
+ for l in string.gmatch(file, "([^\n]*)\n?") do
+ if string.find ( l, "\\%s*$" ) then
+ skip_lines[#skip_lines+1] = string.match(l, "^(.*)\\%s*$")
+ l = nil
+ else
+ if #skip_lines then
+ skip_lines[#skip_lines+1] = l
+ l = table.concat(skip_lines, " ")
+ end
+ -- check if comment line
+ if not string.find ( l, "^%s*#" ) then
+ -- find name
+ local a = string.match ( l, "^%s*([^=]*%S)%s*=" )
+ if a and (search_name == a) then
+ -- Figure out the value
+ local b = string.match ( l, '=%s*(.*%S)%s*$' ) or ""
+ -- remove comments from end of line
+ if string.find ( b, '#' ) then
+ b = string.match ( b, '^(.*%S)%s*#.*$' ) or ""
+ end
+
+ -- We found the name, change the value
+ if not value then
+ l = nil
+ elseif type(value) == "string" then
+ if value ~= b then
+ l = search_name.."="..value
+ end
+ value = nil
+ else
+ local temp = l
+ l = nil
+ for i,val in ipairs(value) do
+ if val == b then
+ l = temp
+ table.remove(value, i)
+ break
+ end
+ end
+ end
+ skip_lines = {} -- replacing line
+ end
+ end
+ if #skip_lines > 0 then
+ for i,line in ipairs(skip_lines) do
+ new_conf_file[#new_conf_file + 1] = line
+ end
+ skip_lines = {}
+ l = nil
+ end
+ end
+ new_conf_file[#new_conf_file + 1] = l
+ end
+
+ if value then
+ -- we didn't find the searchname, add it now
+ if type(value) == "string" then
+ new_conf_file[#new_conf_file + 1] = search_name.."="..value
+ else
+ for i,val in ipairs(value) do
+ new_conf_file[#new_conf_file + 1] = search_name.."="..val
+ end
+ end
+ end
+
+ file = table.concat(new_conf_file, '\n')
+
+ return file, true
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
+-- Parse string for name=value pairs, returned in a table
+local function parse_file (file)
+ if not file or file == "" then
+ return nil
+ end
+ local opts = nil
+ local skip_lines = {}
+ for l in string.gmatch(file, "([^\n]*)\n?") do
+ if string.find ( l, "\\%s*$" ) then
+ skip_lines[#skip_lines+1] = string.match(l, "^(.*)\\%s*$")
+ else
+ if #skip_lines then
+ skip_lines[#skip_lines+1] = l
+ l = table.concat(skip_lines, " ")
+ skip_lines = {}
+ end
+ -- check if comment line
+ if not string.find ( l, "^%s*#" ) then
+ -- find name
+ local a = string.match ( l, "^%s*([^=]*%S)%s*=" )
+ if a then
+ -- Figure out the value
+ local b = string.match ( l, '=%s*(.*%S)%s*$' ) or ""
+ -- remove comments from end of line
+ if string.find ( b, '#' ) then
+ b = string.match ( b, '^(.*%S)%s*#.*$' ) or ""
+ end
+
+ if not (opts) then opts = {} end
+ if not opts[a] then
+ opts[a] = {b}
+ else
+ table.insert(opts[a], b)
+ end
+ end
+ end
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
+
+ return opts
+end
+
+local function validateconfig(config)
+ local success = true
+
+ function testlist(param, test, errtxt)
+ if #param.value > 0 then
+ for i,val in ipairs(param.value) do
+ if test(val) then
+ param.errtxt = errtxt
+ success = false
+ break
+ end
+ end
end
end
- return success, domain
+
+ testlist(config.value.domain, function(v) return string.find(v, "%s") end, "Cannot contain spaces")
+ testlist(config.value.interface, function(v) return string.find(v, "%W") end, "Illegal character")
+ testlist(config.value.listen_address, function(v) return not validator.is_ipv4(v) end, "Invalid IP Address")
+ testlist(config.value.dhcp_range, function(v) return string.find(v, "%s") end, "Cannot contain spaces")
+ testlist(config.value.no_dhcp_interface, function(v) return string.find(v, "%W") end, "Illegal character")
+ testlist(config.value.dhcp_host, function(v) return string.find(v, "%s") end, "Cannot contain spaces")
+ testlist(config.value.dhcp_option, function(v) return string.find(v, "%s") end, "Cannot contain spaces")
+ testlist(config.value.mx_host, function(v) return string.find(v, "%s") end, "Cannot contain spaces")
+
+ return success, config
end
-- ################################################################################
@@ -64,15 +181,19 @@ function getstatus()
end
function getconfig()
- local conf = format.parse_ini_file(fs.read_file(configfile), "") or {}
-require ("html")
+ local conf = parse_file(fs.read_file(configfile) or "") or {}
local output = {}
- output.DOMAIN = cfe({ value = conf.domain or "private.net", label="Local Domain to use",
- descr="Internal Domain for your LAN" })
- output.INTERFACE = cfe({ value=conf.interface , label="Interface" })
- output.IP = cfe({ value=conf["listen-address"] or "", label="IP address to listen on" })
- output.RANGE = cfe ({value=conf["dhcp-range"] or "169.254.0.10,169.254.0.100,255.255.255.0,12h", label="Range of IPs", descr="First,Last,Netmask,Time in hours"})
+ output.domain = cfe({ type="list", value=conf.domain or {}, label="Local Domain",
+ descr="List of internal domain(s) for your LAN 'domain[,address_range]'. Address range can be a single IP address, a range specified by 'IP,IP', or IP/netmask." })
+ output.interface = cfe({ type="list", value=conf.interface or {}, label="Interface", descr="List of interfaces to listen on." })
+ output.listen_address = cfe({ type="list", value=conf["listen-address"] or {}, label="List of IP addresses to listen on" })
+ output.dhcp_range = cfe ({ type="list", value=conf["dhcp-range"] or {}, label="Range of DHCP IPs",
+ descr="List of Start,End,Netmask,Time in seconds/minutes(m)/hours(h) ie. 169.254.0.10,169.254.0.100,255.255.255.0,12h" })
+ output.no_dhcp_interface = cfe({ type="list", value=conf["no-dhcp-interface"] or {}, label="No DHCP Interface", descr="List of interfaces which should have DNS but not DHCP." })
+ output.dhcp_host = cfe({ type="list", value=conf["dhcp-host"] or {}, label="DHCP Host Parameter", descr="List of per host parameters for the DHCP server. See dnsmasq documentation." })
+ output.dhcp_option = cfe({ type="list", value=conf["dhcp-option"] or {}, label="DHCP Option", descr="List of different or extra options to DHCP clients. See dnsmasq documentation." })
+ output.mx_host = cfe({ type="list", value=conf["mx-host"] or {}, label="MX Record", descr="List of MX records 'mx_name,hostname'." })
-- APP.logevent(html.cfe_unpack(output))
@@ -84,10 +205,14 @@ function setconfig(config)
if success then
local file = fs.read_file(configfile)
- file = format.update_ini_file(file,"","domain",config.value.DOMAIN.value)
- file = format.update_ini_file(file,"","interface",config.value.INTERFACE.value)
- file = format.update_ini_file(file,"","listen-address",config.value.IP.value)
- file = format.update_ini_file(file,"","dhcp-range",config.value.RANGE.value)
+ file = update_file(file,"domain",config.value.domain.value)
+ file = update_file(file,"interface",config.value.interface.value)
+ file = update_file(file,"listen-address",config.value.listen_address.value)
+ file = update_file(file,"dhcp-range",config.value.dhcp_range.value)
+ file = update_file(file,"no-dhcp-interface",config.value.no_dhcp_interface.value)
+ file = update_file(file,"dhcp-host",config.value.dhcp_host.value)
+ file = update_file(file,"dhcp-option",config.value.dhcp_option.value)
+ file = update_file(file,"mx-host",config.value.mx_host.value)
fs.write_file(configfile, file)
else
config.errtxt = "Failed to set config"
@@ -106,132 +231,3 @@ function setconfigfile(filedetails)
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
- local f = io.popen("rm "..baseurl.."ip/"..name)
- f:close()
- end
- end
- for name in pairs(reverseIPs) do
- -- need to create the file
- local f = io.popen("touch "..baseurl.."ip/"..name)
- f:close()
- 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
- local f = io.popen("touch "..baseurl.."servers/"..domain.value.domain.value)
- f:close()
- 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)
- 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
- local f = io.popen("rm "..baseurl.."servers/"..name)
- f:close()
- cmdresult.errtxt = nil
- cmdresult.value = "Domain deleted"
- break
- end
- end
- end
- return cmdresult
-end