-- acf model for /etc/dhcp/* -- Copyright(c) 2007 A. Brodmann - Licensed under terms of GPL2 module (..., package.seeall) --- get additional libraries require("procps") require("validator") require("daemoncontrol") require("procps") local subnet = {} local cfgdir = "/etc/dhcp/" local processname = "dhcpd" local packagename = "dhcp" -- ################################################################################ -- LOCAL FUNCTIONS local function get_version() local cmd = "/sbin/apk_version -v -s " .. packagename .. " | cut -d ' ' -f 1" local cmd_output = io.popen( cmd ) local cmd_output_result = cmd_output:read("*a") or "" cmd_output:close() return cmd_output_result end local function autostarts() local cmd_output_result local cmd = "/sbin/rc_status | egrep '^S' | egrep '" .. processname .."' 2>/dev/null" local f = io.popen( cmd ) local cmdresult = f:read("*a") if (cmdresult) and (#cmdresult > 0) then cmd_output_result = "Process will autostart at next boot (at sequence '" .. string.match(cmdresult,"^%a+(%d%d)") .. "')" else cmd_output_error = "Not programmed to autostart" end f:close() return cmd_output_result, cmd_output_error end function process_status_text(procname) local t = procps.pidof(procname) if (t) and (#t > 0) then return "Enabled" else return "Disabled" end end -- ################################################################################ -- PUBLIC FUNCTIONS function getstatus () local status = {} status.version = cfe({ name = "version", label="Program version", value=get_version(), }) status.status = cfe({ name="status", label="Program status", value=process_status_text(processname), }) local autostart_sequense, autostart_errtxt = autostarts() status.autostart = cfe({ name="autostart", label="Autostart sequence", value=autostart_sequense, errtxt=autostart_errtxt, }) return status end --- the tokenizer functions - must be dislocated into a library later tokenizer = {} tokenizer.new = function( str, delim ) local token = {} token.value = str; token.delim = delim; token.pos = 1 return token end tokenizer.pos = function( value, substr, pos ) local retval = pos local done = false while not done and retval <= #value do if string.sub( value, retval, retval ) == substr then done = true else retval = retval + 1 end end return retval end tokenizer.next = function( token ) if token.pos > #token.value then return token, nil end local strpos = tokenizer.pos( token.value, token.delim, token.pos ) retval = string.sub(token.value, token.pos, strpos-1) if retval == token.delim then retval = "" token.pos = token.pos + 1 else token.pos = strpos + 1 end return token, retval end --- dep_check = function () icode = 0 retval = {} lpos = require "posix" ptr, msg, code = lpos.access("/etc/dhcp") if ptr == nil then table.insert(retval, "/etc/dhcp") icode = icode + 1 end ptr, msg, code = lpos.access("/usr/sbin/dhcpd") if ptr == nil then table.insert(retval, "/usr/sbin/dhcpd") icode = icode + 1 end ptr, msg, code = lpos.access("/etc/init.d/dhcpd") if ptr == nil then table.insert(retval, "/etc/init.d/dhcpd") icode = icode + 1 end if icode == 0 then retval = nil end return retval end config_generate = function() msg = "" tmpfilename = os.tmpname() -- create tmp config file local tmpfile = io.open( tmpfilename, "w+" ) -- get, validate and write global settings to tmp config file settings = read_settings() s_msg, s_fields = validate_settings( settings ) if #s_msg > 0 then tmpfile:close() os.remove( tmpfilename ) msg = "Configuration Generation Failed!\n\n" .. "Reason: Error in Global Settings\n" return msg end tmpfile:write( "authoritative;\n" ) tmpfile:write( "ddns-update-style none;\n\n" ) tmpfile:write( "option local-wpad-server code 252 = text;\n\n" ) tmpfile:write( "include \"/etc/dhcp/dhcpd.preconfig\";\n" ) if #settings.domainname.value > 0 then tmpfile:write( "option domain-name \"" .. settings.domainname.value .. "\";\n" ) end tmpfile:write( "default-lease-time " .. settings.defleasetime.value .. ";\n" ) tmpfile:write( "max-lease-time " .. settings.maxleasetime.value .. ";\n\n" ) -- get, validate and write subnet configurations to tmp config file tmpfile:write( "###### SUBNET CONFIG BEGIN ######\n\n" ) subnets = get_subnets() local numnetworks = 0 for k,v in ipairs(subnets) do numnetworks = numnetworks + 1 net = subnet_read( v ) sn_msg, sn_fields = validate_network( net ) if #sn_msg > 0 then tmpfile:close() os.remove( tmpfilename ) msg = "Configuration Generation Failed!\n\n" .. "Reason: Error in Subnet '" .. v .. "'\n" return msg end tmpfile:write( "# " .. net.name.value .. "\n" ) tmpfile:write( "subnet " .. net.subnet.value .. " netmask " .. net.netmask.value .. " {\n" ) if #net.defleasetime.value > 0 then tmpfile:write( " default-lease-time " .. net.defleasetime.value .. ";\n" ) end if #net.maxleasetime.value > 0 then tmpfile:write( " max-lease-time " .. net.maxleasetime.value .. ";\n" ) end tmpfile:write( " option routers " .. net.gateway.value .. ";\n" ) dnssrvrs = "" if #net.dnssrv1.value > 0 then dnssrvrs = net.dnssrv1.value end if #net.dnssrv2.value > 0 then if #dnssrvrs > 0 then dnssrvrs = dnssrvrs .. ", " .. net.dnssrv2.value else dnssrvrs = net.dnssrv2.value end end if #dnssrvrs > 0 then tmpfile:write( " option domain-name-servers " .. dnssrvrs .. ";\n" ) end if #net.domainname.value > 0 then tmpfile:write( " option domain-name \"" .. net.domainname.value .. "\";\n" ) end if #net.wpad.value > 0 then tmpfile:write( " option local-wpad-server \"" .. net.wpad.value .. "\\n\";\n" ) end spec2_msg = generate_pool( tmpfile, tmpfilename, net ) if #spec2_msg > 0 then tmpfile:close() os.remove( tmpfilename ) msg = "Configuration Generation Failed!\n\n" .. spec2_msg return msg end --- generate advanced part / drop in advancedfile = io.open( cfgdir .. net.name.value .. ".advanced", "r" ) if advancedfile ~= nil then nxtline = advancedfile:read( "*l" ) while nxtline ~= nil do tmpfile:write( " " .. nxtline .. "\n" ) nxtline = advancedfile:read( "*l" ) end advancedfile:close() end --- tmpfile:write( "}\n\n" ) end tmpfile:write( "###### SUBNET CONFIG END ######\n\n" ) if numnetworks <= 0 then tmpfile:close() os.remove( tmpfilename ) msg = "Configuration Generation Failed!\n\n" .. "Reason: No Subnets defined!\n" return msg end msg = generate_hosts( tmpfile ) if #msg > 0 then tmpfile:close() os.remove( tmpfilename ) return msg end tmpfile:write( "include \"/etc/dhcp/dhcpd.postconfig\";\n" ) tmpfile:close() os.rename( tmpfilename, "/etc/dhcp/dhcpd.conf" ) -- make sure the master pre/post config files are present local precfg local postcfg precfg = io.open( "/etc/dhcp/dhcpd.preconfig", "r" ) postcfg = io.open( "/etc/dhcp/dhcpd.postconfig", "r" ) if precfg == nil then precfg = io.open( "/etc/dhcp/dhcpd.preconfig", "w+" ) end precfg:close() if postcfg == nil then postcfg = io.open( "/etc/dhcp/dhcpd.postconfig", "w+" ) end postcfg:close() return "Configuration Generation Successful!\n" end generate_pool = function( tmpfile, tmpfilename, net ) if not validator.is_ipv4( net.leaserangestart.value ) or not validator.is_ipv4( net.leaserangeend.value ) then if net.unknownclients.value == "allow" then msg = "Reason: permitted unknown clients but failed to define lease range!\n" return msg end return "" end --- pool header tmpfile:write( " pool {\n" ) if net.unknownclients.value == "allow" then tmpfile:write( " allow known-clients;\n" ) tmpfile:write( " allow unknown-clients;\n" ) else tmpfile:write( " allow known-clients;\n" ) tmpfile:write( " deny unknown-clients;\n" ) end tmpfile:write( " range " .. net.leaserangestart.value .. " " .. net.leaserangeend.value .. ";\n" ) tmpfile:write( " }\n" ) return "" end generate_hosts = function( outfile ) local retval = "" outfile:write( "\n####### STATIC HOSTS BEGIN ######\n\n" ) snets = get_subnets() for k,v in ipairs(snets) do msg = generate_hosts_persubnet( outfile, v ) if #msg > 0 then return msg end end outfile:write( "###### STATIC HOSTS END ######\n" ) retval = generate_hosts_dynamic( outfile ) return retval end generate_hosts_persubnet = function( outfile, netname ) local retval = "" local hostsfile = io.open( cfgdir .. netname .. ".static", "r" ) if hostsfile ~= nil then local hostsdata = hostsfile:read( "*a" ) hostsfile:close() if hostsdata ~= nil then if #hostsdata <= 0 then return retval end outfile:write( "# " .. netname .. "\n" ) outfile:write( "group {\n" ) local done = false local hosttoken = tokenizer.new( hostsdata, "\n" ) while not done do hosttoken, nexthost = tokenizer.next( hosttoken ) if nexthost ~= nil then if string.sub( nexthost, 1, 1) ~= "#" then local spectoken = tokenizer.new( nexthost, ";" ) spectoken, hostname = tokenizer.next( spectoken ) spectoken, ip = tokenizer.next( spectoken ) spectoken, mac = tokenizer.next( spectoken ) spectoken, comment = tokenizer.next( spectoken ) outfile:write(" host " .. hostname .. " {\n") outfile:write(" hardware ethernet " .. mac .. ";\n") outfile:write(" fixed-address " .. ip .. ";\n") outfile:write(" }\n") end else done = true end end outfile:write( "}\n\n" ) else retval = "Configuration Generation Failed: Failed to read data from subnet static hosts file for " .. netname end end return retval end generate_hosts_dynamic = function( outfile ) local retval = "" local hostsfile = io.open( cfgdir .. "dhcpd.dynamic", "r" ) if hostsfile ~= nil then local hostsdata = hostsfile:read( "*a" ) hostsfile:close() if hostsdata ~= nil then if #hostsdata <= 0 then return retval end outfile:write( "group {\n" ) local done = false local hosttoken = tokenizer.new( hostsdata, "\n" ) while not done do hosttoken, nexthost = tokenizer.next( hosttoken ) if nexthost ~= nil then if string.sub( nexthost, 1, 1) ~= "#" then local spectoken = tokenizer.new( nexthost, ";" ) spectoken, hostname = tokenizer.next( spectoken ) spectoken, mac = tokenizer.next( spectoken ) spectoken, comment = tokenizer.next( spectoken ) outfile:write(" host " .. hostname .. " {\n") outfile:write(" hardware ethernet " .. mac .. ";\n") outfile:write(" }\n") end else done = true end end outfile:write( "}\n" ) else retval = "Configuration Generation Failed: Failed to read data from dynamic hosts file!" end end return retval end subnet_delete = function( name ) local msg = "" local filename = cfgdir .. name os.remove( filename .. ".subnet" ) os.remove( filename .. ".static" ) os.remove( filename .. ".dynamic" ) return msg end advglobal_read = function() preconfig = "" postconfig = "" dynamic = "" file = io.open( cfgdir .. "dhcpd.preconfig", "r" ) if file ~= nil then preconfig = file:read( "*a" ) if preconfig == nil then preconfig = "" end file:close() end file = io.open( cfgdir .. "dhcpd.postconfig", "r" ) if file ~= nil then postconfig = file:read( "*a" ) if postconfig == nil then postconfig = "" end file:close() end file = io.open( cfgdir .. "dhcpd.dynamic", "r" ) if file ~= nil then dynamic = file:read( "*a" ) if dynamic == nil then dynamic = "" end file:close() end return cfe({ preconfig = preconfig, postconfig = postconfig, dynamic = dynamic }) end advglobal_update = function( preconfig, postconfig, dynamic ) file = io.open( cfgdir .. "dhcpd.preconfig", "wb+" ) if file ~= nil then file:write( preconfig ) file:close() end file = io.open( cfgdir .. "dhcpd.postconfig", "wb+" ) if file ~= nil then file:write( postconfig ) file:close() end file = io.open( cfgdir .. "dhcpd.dynamic", "wb+" ) if file ~= nil then file:write( dynamic ) file:close() end return cfe({ preconfig = preconfig, postconfig = postconfig, dynamic = dynamic }) end subnet_read = function( name ) local filename = cfgdir .. name .. ".subnet" local net = create_new_net( name, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil ) for line in io.lines(filename) do if (string.sub(line, 1, 15) == "def-lease-time:") then net.defleasetime.value = string.sub(line, 17) elseif (string.sub(line, 1, 15) == "max-lease-time:") then net.maxleasetime.value = string.sub(line, 17) elseif (string.sub(line, 1, 8) == "gateway:") then net.gateway.value = string.sub(line, 10) elseif (string.sub(line, 1, 12) == "domain-name:") then net.domainname.value = string.sub(line, 14) elseif (string.sub(line, 1, 10) == "dns-srv-1:") then net.dnssrv1.value = string.sub(line, 12) elseif (string.sub(line, 1, 10) == "dns-srv-2:") then net.dnssrv2.value = string.sub(line, 12) elseif (string.sub(line, 1, 7) == "subnet:") then net.subnet.value = string.sub(line, 9) elseif (string.sub(line, 1, 8) == "netmask:") then net.netmask.value = string.sub(line, 10) elseif (string.sub(line, 1, 18) == "lease-range-start:") then net.leaserangestart.value = string.sub(line, 20) elseif (string.sub(line, 1, 16) == "lease-range-end:") then net.leaserangeend.value = string.sub(line, 18) elseif (string.sub(line, 1, 5) == "wpad:") then net.wpad.value = string.sub(line, 7) elseif (string.sub(line, 1, 16) == "unknown-clients:") then net.unknownclients.value = string.sub(line, 18) end end if net.unknownclients.value ~= "allow" then net.unknownclients.value = "deny" end net.statichosts.value = subnet_get_spechosts( name, "static" ) net.advanced.value = subnet_get_spechosts( name, "advanced" ) return net end subnet_get_spechosts = function( name, suffix ) local retval = "" local filename = cfgdir .. name .. "." .. suffix if file_exists( filename ) then local file = io.open( filename, "r" ) if file ~= nil then msg = file:read( "*a" ) if msg ~= nil then retval = msg end file:close() end end return retval end subnet_update_statichosts = function( name, statichosts ) local msg = ""; local filename = cfgdir .. name .. ".static" file, errmsg = io.open( filename, "wb+" ) if file == nil then msg = "Error: Failed to open " .. filename .. "(" .. errmsg .. ")!" else file:write( statichosts ) file:close() end return msg end subnet_update_advanced = function( name, advanced ) local msg = ""; local filename = cfgdir .. name .. ".advanced" file, errmsg = io.open( filename, "wb+" ) if file == nil then msg = "Error: Failed to open " .. filename .. "(" .. errmsg .. ")!" else file:write( advanced ) file:close() end return msg end read_settings = function() local filename = cfgdir .. "globalsettings.conf" local settings = create_new_settings( nil, nil, nil ) if file_exists( filename ) then for line in io.lines(filename) do if (string.sub(line, 1, 15) == "def-lease-time:") then settings.defleasetime.value = string.sub(line, 17) elseif (string.sub(line, 1, 15) == "max-lease-time:") then settings.maxleasetime.value = string.sub(line, 17) elseif (string.sub(line, 1, 12) == "domain-name:") then settings.domainname.value = string.sub(line, 14) end end end return settings end subnet_write = function( net ) msg, fields = validate_network( net ) if #msg > 0 then return cfe({ msg = msg, fields = fields }), net end local filename = cfgdir .. net.name.value .. ".subnet" local file = io.open( filename, "w+" ) file:write( "def-lease-time: " .. net.defleasetime.value .. "\n" ) file:write( "max-lease-time: " .. net.maxleasetime.value .. "\n" ) file:write( "gateway: " .. net.gateway.value .. "\n" ) file:write( "domain-name: " .. net.domainname.value .. "\n" ) file:write( "dns-srv-1: " .. net.dnssrv1.value .. "\n" ) file:write( "dns-srv-2: " .. net.dnssrv2.value .. "\n" ) file:write( "subnet: " .. net.subnet.value .. "\n" ) file:write( "netmask: " .. net.netmask.value .. "\n" ) file:write( "lease-range-start: " .. net.leaserangestart.value .. "\n" ) file:write( "lease-range-end: " .. net.leaserangeend.value .. "\n" ) file:write( "wpad: " .. net.wpad.value .. "\n" ) file:write( "unknown-clients: " .. net.unknownclients.value .. "\n" ) file:close() spec_msg = validate_statichosts( net.statichosts.value ) if #spec_msg == 0 then spec_msg = subnet_update_statichosts( net.name.value, net.statichosts.value ) if #spec_msg > 0 then msg = spec_msg table.insert( fields, "statichosts" ) end else msg = spec_msg table.insert( fields, "statichosts" ) end spec_msg = subnet_update_advanced( net.name.value, net.advanced.value ) if #spec_msg > 0 then msg = spec_msg table.insert( fields, "advanced" ) end return cfe({ msg = msg, fields = {}}), net end validate_statichosts = function( statichosts ) local line = 1 local msg = "" local done = false hosttoken = tokenizer.new( statichosts, "\n") while not done do hosttoken, nexthost = tokenizer.next( hosttoken ) if nexthost ~= nil then if string.sub(nexthost, 1, 1) ~= "#" then fieldtoken = tokenizer.new( nexthost, ";") fieldtoken, hostname = tokenizer.next( fieldtoken ) fieldtoken, ip = tokenizer.next( fieldtoken ) fieldtoken, mac = tokenizer.next( fieldtoken ) fieldtoken, comment = tokenizer.next( fieldtoken ) if hostname == nil then msg = msg .. "Static Hosts: hostname missing on line " .. line .. "!\n" else if not is_valid_hostname( hostname ) then msg = msg .. "Static Hosts: Invalid hostname on line " .. line .. "!\n" end end if ip == nil then msg = msg .. "Static Hosts: ip missing on line " .. line .. "!\n" else if not validator.is_ipv4( ip ) then msg = msg .. "Static Hosts: Invalid ip on line " .. line .. "!\n" end end if mac == nil then msg = msg .. "Static Hosts: mac missing on line " .. line .. "!\n" else if not validator.is_mac( mac ) then msg = msg .. "Static Hosts: Invalid mac on line " .. line .. "!\n" end end end line = line + 1 else done = true end end return msg end validate_dynamichosts = function( dynamichosts ) local line = 1 msg = "" local done = false hosttoken = tokenizer.new( dynamichosts, "\n") while not done do hosttoken, nexthost = tokenizer.next( hosttoken ) if nexthost ~= nil then if string.sub(nexthost, 1, 1) ~= "#" then fieldtoken = tokenizer.new( nexthost, ";") fieldtoken, hostname = tokenizer.next( fieldtoken ) fieldtoken, mac = tokenizer.next( fieldtoken ) fieldtoken, comment = tokenizer.next( fieldtoken ) if hostname == nil then msg = msg .. "Dynamic Hosts: hostname missing on line " .. line .. "!\n" else if not is_valid_hostname( hostname ) then msg = msg .. "Dynamic Hosts: Invalid hostname on line " .. line .. "!\n" end end if mac == nil then msg = msg .. "Dynamic Hosts: mac missing on line " .. line .. "!\n" else if not validator.is_mac( mac ) then msg = msg .. "Dynamic Hosts: Invalid mac on line " .. line .. "!\n" end end end line = line + 1 else done = true end end return msg end update_settings = function ( settings ) msg, fields = validate_settings ( settings ) if #msg > 0 then return cfe({ msg = msg, fields = fields }), settings end local filename = cfgdir .. "globalsettings.conf" local file = io.open( filename, "w+" ) file:write( "def-lease-time: " .. settings.defleasetime.value .. "\n" ) file:write( "max-lease-time: " .. settings.maxleasetime.value .. "\n" ) file:write( "domain-name: " .. settings.domainname.value .. "\n" ) return cfe({ msg = "", fields = {}}), settings end subnet_create = function( net ) if file_exists( cfgdir .. net.name.value .. ".subnet" ) then return cfe({ msg = "This subnet already exists!", fields = {}}), net end retcode, net = subnet_write( net ) return retcode, net end _tonumber = function( value ) ret = tonumber( value ) if (ret == nil) then ret = 0 end return ret end validate_network = function( net ) fields = {} msg = "" if #net.name.value < 3 then table.insert(fields, "name") msg = msg .. "Minimum network name length is 3 characters!\n" end if not is_valid_netname( net.name.value ) then table.insert( fields, "name" ) msg = msg .. "Invalid network name: allowed characters are: 'a..z', '0..9', '-'\n" end if net.name.value == "" then table.insert(fields, "name") msg = msg .. "<new> is not a valid network name!\n" end if #net.defleasetime.value > 0 then if not validator.is_integer_in_range(_tonumber(net.defleasetime.value), 1800, 86400) then table.insert(fields, "defleasetime") msg = msg .. "Default-Lease-Time must be: 1800 < x < 86400\n" end end if #net.maxleasetime.value > 0 then if not validator.is_integer_in_range(_tonumber(net.maxleasetime.value), 1800, 86400) then table.insert(fields, "maxleasetime") msg = msg .. "Maximum-Lease-Time must be: 1800 < x < 86400\n" end end if not validator.is_ipv4(net.gateway.value) then table.insert(fields, "gateway") msg = msg .. "Gateway: invalid IPv4 address!\n" end if #net.dnssrv1.value > 0 then if not validator.is_ipv4(net.dnssrv1.value) then table.insert(fields, "dnssrv1") msg = msg .. "DNS Server 1: invalid IPv4 address!\n" end end if not validator.is_ipv4(net.dnssrv2.value) then if #net.dnssrv2.value > 0 then table.insert(fields, "dnssrv2") msg = msg .. "DNS Server 2: invalid IPv4 address!\n" end end if not validator.is_ipv4(net.subnet.value) then table.insert(fields, "subnet") msg = msg .. "Subnet: invalid IPv4 address!\n" end if not validator.is_ipv4(net.netmask.value) then table.insert(fields, "netmask") msg = msg .. "Netmask: invalid IPv4 address!\n" end if #net.leaserangestart.value > 0 then if not validator.is_ipv4(net.leaserangestart.value) then table.insert(fields, "leaserangestart") msg = msg .. "Lease-Range-Start: invalid IPv4 address!\n" end end if #net.leaserangeend.value > 0 then if not validator.is_ipv4(net.leaserangeend.value) then table.insert(fields, "leaserangeend") msg = msg .. "Lease-Range-End: invalid IPv4 address!\n" end end return msg, fields end file_exists = function( filename ) retval = false lpos = require "posix" ptr, msg, code = lpos.access( filename ) if ptr ~= nil then retval = true end return retval end read_file = function ( filename ) local contents = "" local line = "" local file = io.open( filename, "r" ) if file ~= nil then line = file:read( "*l" ) while line ~= nil do contents = contents .. "\n" .. line line = file:read( "*l" ) end file:close() else contents = "\n Error: File not found!\n\n" end return contents end is_running = function( process ) return procps.pidof(process) ~= nil end get_dhcpd_version = function() local retval = "dhcpd" local file = io.popen("/usr/sbin/dhcpd --version 2>&1") if file ~= nil then local line = file:read( "*a" ) if #line > 0 then retval = line end file:close() end return retval end service_control = function ( command ) local retval = "" local code = false local x local y code, retval, x, y = daemoncontrol.daemoncontrol ( "dhcpd", command ) return retval end function nonil( value ) local retval = "" if value ~= nil then retval = value end return retval end get_subnets = function () local retval = {} lpos = require "posix" files = lpos.dir( "/etc/dhcp" ) for k,v in ipairs(files) do if string.sub(v, -7) == ".subnet" then table.insert(retval, string.sub(v, 1, -8)) end end return retval end create_new_net = function( name, defleasetime, maxleasetime, gateway, domainname, dnssrv1, dnssrv2, subnet, netmask, leaserangestart, leaserangeend, wpad, statichosts, unknownclients, dynamichosts, advanced, useadvanced ) net = { name = { label="Name", value=nonil(name), type="message" }, defleasetime = { label="Default Lease Time", value=nonil(defleasetime), type="text" }, maxleasetime = { label="Maximum Lease Time", value=nonil(maxleasetime), type="text" }, gateway = { label="Gateway", value=nonil(gateway), type="text" }, domainname = { label="Domainname", value=nonil(domainname), type="text" }, dnssrv1 = { label="DNS Server 1", value=nonil(dnssrv1), type="text" }, dnssrv2 = { label="DNS Server 2", value=nonil(dnssrv2), type="text" }, subnet = { label="Subnet", value=nonil(subnet), type="text" }, netmask = { label="Netmask", value=nonil(netmask), type="text" }, leaserangestart = { label="Lease Range Start", value=nonil(leaserangestart), type="text" }, leaserangeend = { label="Lease Range End", value=nonil(leaserangeend), type="text" }, wpad = { label="Web Proxy Auto Discovery", value=nonil(wpad), type="text" }, statichosts = { label="Static Hosts", value=nonil(statichosts), type="text" }, dynamichosts = { label="Dynamic Hosts", value=nonil(dynamichosts), type="text" }, unknownclients = { label="Unknown Clients", value=nonil(unknownclients), type="text" }, advanced = { label="Advanced", value=nonil(advanced), type="text" }, useadvanced = { label="Use Advanced", value=nonil(useadvanced), type="text" } } if net.unknownclients.value ~= "allow" then net.unknownclients.value = "deny" end return net end create_new_settings = function( defleasetime, maxleasetime, domainname ) settings = { domainname = { label="Domainname", type="text", value=nonil(domainname) }, defleasetime = { label="Default Lease Time", type="text", value=nonil(defleasetime) }, maxleasetime = { label="Maximum Lease Time", type="text", value=nonil(maxleasetime) } } return settings end validate_settings = function ( settings ) msg = "" fields = {} if not validator.is_integer_in_range(_tonumber(settings.defleasetime.value), 1800, 86400) then msg = msg .. "Default Lease Time: Out of range 1800 < x < 86400 or not integer\n" table.insert( fields, "defleasetime" ) end if not validator.is_integer_in_range(_tonumber(settings.maxleasetime.value), 1800, 86400) then msg = msg .. "Maximum Lease Time: Out of range 1800 < x < 86400 or not integer\n" table.insert( fields, "maxleasetime" ) end if not is_valid_hostname( settings.domainname.value ) then if #settings.domainname.value > 0 then msg = msg .. "Invalid domainname: valid chars are 'a..z', '0..9', '.', '-'\n" table.insert( fields, "domainname" ) end end return msg, fields end is_valid_hostname = function ( hostname ) local retval = true name = string.lower( hostname ) lap = 1 while lap <= #name do chr = string.sub(name, lap, lap) if (chr >= "a" and chr <= "z") or (chr >= "0" and chr <= "9") or (chr == ".") or (chr == "-") then else retval = false end lap = lap + 1 end return retval end is_valid_netname = function ( netname ) local retval = true name = string.lower( netname ) lap = 1 while lap <= #name do chr = string.sub( name, lap, lap ) if (chr >= "a" and chr <= "z") or (chr >= "0" and chr <= "9") or (chr == "-") then else retval = false end lap = lap + 1 end return retval end