if type(action) == 'function' then
action = action(g)
end
- return actid, action, filter
+ return actid, action, filter
end
-- Compile a rule described by query language
return filter(req, qry) and action
end
else
- p = function (req, qry)
+ p = function ()
return action
end
end
-- @function Remove a rule
function M.del(id)
- for i, r in ipairs(M.rules) do
+ for _, r in ipairs(M.rules) do
if r.rule.id == id then
policy.del(id)
table.remove(M.rules, id)
-- @function Find a rule
function M.get(id)
- for i, r in ipairs(M.rules) do
+ for _, r in ipairs(M.rules) do
if r.rule.id == id then
return r
end
-- @function Enable/disable a rule
function M.toggle(id, val)
- for i, r in ipairs(M.rules) do
+ for _, r in ipairs(M.rules) do
if r.rule.id == id then
r.rule.suspended = not val
return true
end
-- @function Enable/disable a rule
-function M.disable(id, val)
+function M.disable(id)
return M.toggle(id, false)
end
-function M.enable(id, val)
+function M.enable(id)
return M.toggle(id, true)
end
end
-- @function Publish DAF statistics
-local function publish(h, ws)
+local function publish(_, ws)
local cqueues = require('cqueues')
local ok, last = true, nil
while ok do
end
-- @function Configure module
-function M.config(conf)
+function M.config()
if not http or not http.endpoints then return end
-- Export API and data publisher
http.endpoints['/daf.js'] = http.page('daf.js', 'daf')
-- Module interface
local ffi = require('ffi')
-local bit = require('bit')
local mod = {}
local addr_buf = ffi.new('char[16]')
-- Config
if state == kres.FAIL then return state end
pkt = kres.pkt_t(pkt)
req = kres.request_t(req)
- qry = req:current()
+ local qry = req:current()
-- Observe only authoritative answers
if mod.proxy == nil or not qry.flags.RESOLVED then
return state
extraFlags.DNSSEC_WANT = qry.flags.DNSSEC_WANT
extraFlags.AWAIT_CUT = true
extraFlags.DNS64_MARK = true
- local next = req:push(pkt:qname(), kres.type.A, kres.class.IN, extraFlags, qry)
+ req:push(pkt:qname(), kres.type.A, kres.class.IN, extraFlags, qry)
end
end
return state
end
end
-function M.init(module)
+function M.init()
M.ev = nil
M.cli = {}
M.info = {}
return 0
end
-function M.deinit(module)
+function M.deinit()
if M.ev then event.cancel(M.ev) end
return 0
end
end
-- @function Make connection to Graphite server.
-function M.add_server(graphite, host, port, tcp)
- local s, err = tcp and make_tcp(host, port) or make_udp(host, port)
+function M.add_server(_, host, port, tcp)
+ local s, err
+ if tcp then
+ s, err = make_tcp(host, port)
+ else
+ s, err = make_udp(host, port)
+ end
if not s then
error(err)
end
if conf.prefix then M.prefix = conf.prefix end
-- connect to host(s)
if type(conf.host) == 'table' then
- for key, val in pairs(conf.host) do
- graphite:add_server(val, conf.port, conf.tcp)
+ for _, val in pairs(conf.host) do
+ M:add_server(val, conf.port, conf.tcp)
end
else
- graphite:add_server(conf.host, conf.port, conf.tcp)
+ M:add_server(conf.host, conf.port, conf.tcp)
end
-- start publishing stats
if M.ev then event.cancel(M.ev) end
local function serve_root()
local data = pgload('main.tpl')[2]
data = data
- :gsub('{{ title }}', title or ('kresd @ ' .. hostname()))
+ :gsub('{{ title }}', M.title or ('kresd @ ' .. hostname()))
:gsub('{{ host }}', hostname())
- return function (h, stream)
+ return function (_, stream)
-- Render snippets
local rsnippets = {}
for _,v in pairs(M.snippets) do
end
-- Check loaded certificate
if not crt or not key then
- panic('failed to load certificate "%s" - %s', crtfile, err or 'error')
+ panic('failed to load certificate "%s"', crtfile)
end
end
-- Compose server handler
if crt and ephemeral then
local _, expiry = crt:getLifetime()
expiry = math.max(0, expiry - (os.time() - 3 * 24 * 3600))
- event.after(expiry, function (ev)
+ event.after(expiry, function ()
log('[http] refreshed ephemeral certificate')
crt, key = updatecert(crtfile, keyfile)
s.ctx = tlscontext(crt, key)
return t
end
-local function snapshot_end(h, ws)
+local function snapshot_end()
snapshots_count = false
end
-- Function to sort frequency list
-local function snapshot_start(h, ws)
- local ok, prev = true, getstats()
+local function snapshot_start()
+ local prev = getstats()
while snapshots_count do
local is_empty = true
-- Get current snapshot
local wdata = {}
for _, info in pairs(map 'worker.info()') do
if type(info) == 'table' then
- wdata[tostring(info.pid)] = {rss=info.rss, usertime=info.usertime, systime=info.systime, pagefaults=info.pagefaults, queries=info.queries}
+ wdata[tostring(info.pid)] = {
+ rss = info.rss,
+ usertime = info.usertime,
+ systime = info.systime,
+ pagefaults = info.pagefaults,
+ queries = info.queries
+ }
end
end
-- Publish stats updates periodically
end
-- Function to sort frequency list
-local function stream_stats(h, ws)
+local function stream_stats(_, ws)
-- Initially, stream history
local ok, last = true, nil
local batch = {}
-- @function update subtree configuration
local function update_subtree(tree)
if not tree then return end
- for i,k in pairs(tree) do
+ for _, k in pairs(tree) do
if k.dir then
update_subtree(k.nodes)
else
-- @function reload whole configuration
function ketcd.reload()
- local ketcd = _G['ketcd']
local res, err = ketcd.cli:readdir('/', true)
if err then
error(err)
end
- update_subtree(res.body.node.nodes)
+ update_subtree(res.body.node.nodes)
end
-function ketcd.init(module)
+function ketcd.init()
ketcd.Etcd = require('etcd.luasocket')
ketcd.defaults = { prefix = '/kresd' }
end
-function ketcd.deinit(module)
+function ketcd.deinit()
if ketcd.ev then event.cancel(ketcd.ev) end
end
-- create connection
local cli, err = ketcd.Etcd.new(options)
if err then
- error(err)
+ error(err)
end
ketcd.cli = cli
-- schedule recurrent polling
local kres = require('kres')
-local bit = require('bit')
local ffi = require('ffi')
local todname = kres.str2dname -- not available during module load otherwise
-- String address@port -> sockaddr.
local function addr2sock(target)
local addr, port = addr_split_port(target)
- sock = ffi.gc(ffi.C.kr_straddr_socket(addr, port), ffi.C.free);
+ local sock = ffi.gc(ffi.C.kr_straddr_socket(addr, port), ffi.C.free);
if sock == nil then
error("target '"..target..'" is not a valid IP address')
end
-- Set and clear some query flags
local function flags(opts_set, opts_clear)
- return function(state, req)
+ return function(_, req)
local qry = req:current()
ffi.C.kr_qflags_set (qry.flags, kres.mk_qflags(opts_set or {}))
ffi.C.kr_qflags_clear(qry.flags, kres.mk_qflags(opts_clear or {}))
local dname_localhost = todname('localhost.')
-- Rule for localhost. zone; see RFC6303, sec. 3
-local function localhost(state, req)
+local function localhost(_, req)
local qry = req:current()
local answer = req.answer
ffi.C.kr_pkt_make_auth_header(answer)
-- Answer with locally served minimal 127.in-addr.arpa domain, only having
-- a PTR record in 1.0.0.127.in-addr.arpa, and with 1.0...0.ip6.arpa. zone.
-- TODO: much of this would better be left to the hints module (or coordinated).
-local function localhost_reversed(state, req)
+local function localhost_reversed(_, req)
local qry = req:current()
local answer = req.answer
-- All requests
function policy.all(action)
- return function(req, query) return action end
+ return function(_, _) return action end
end
-- Requests which QNAME matches given zone list (i.e. suffix match)
function policy.suffix(action, zone_list)
local AC = require('ahocorasick')
local tree = AC.create(zone_list)
- return function(req, query)
+ return function(_, query)
local match = AC.match(tree, query:name(), false)
if match ~= nil then
return action
function policy.suffix_common(action, suffix_list, common_suffix)
local common_len = string.len(common_suffix)
local suffix_count = #suffix_list
- return function(req, query)
+ return function(_, query)
-- Preliminary check
local qname = query:name()
if not string.find(qname, common_suffix, -common_len, true) then
-- Filter QNAME pattern
function policy.pattern(action, pattern)
- return function(req, query)
+ return function(_, query)
if string.find(query:name(), pattern) then
return action
end
if not parser:open(path) then error(string.format('failed to parse "%s"', path)) end
while parser:parse() do
local name = ffi.string(parser.r_owner, parser.r_owner_length)
- local action = ffi.string(parser.r_data, parser.r_data_length)
- rules[name] = action_map[action]
+ local name_action = ffi.string(parser.r_data, parser.r_data_length)
+ rules[name] = action_map[name_action]
-- Warn when NYI
- if #name > 1 and not action_map[action] then
+ if #name > 1 and not action_map[name_action] then
print(string.format('[ rpz ] %s:%d: unsupported policy action', path, tonumber(parser.line_counter)))
end
end
local function rpz_zonefile(action, path)
local rules = rpz_parse(action, path)
collectgarbage()
- return function(req, query)
+ return function(_, query)
local label = query:name()
- local action = rules[label]
- while action == nil and string.len(label) > 0 do
+ local rule = rules[label]
+ while rule == nil and string.len(label) > 0 do
label = string.sub(label, string.byte(label) + 2)
- action = rules['\1*'..label]
+ rule = rules['\1*'..label]
end
- return action
+ return rule
end
end
policy.layer = {
begin = function(state, req)
req = kres.request_t(req)
- return policy.evaluate(policy.rules, req, req:current(), state) or
+ return policy.evaluate(policy.rules, req, req:current(), state) or
policy.evaluate(policy.special_names, req, req:current(), state) or
state
- end,
+ end,
finish = function(state, req)
req = kres.request_t(req)
return policy.evaluate(policy.postrules, req, req:current(), state) or state
todname('arpa.')),
count=0
},
-
}
return policy
-- Module interface
-local policy = require('policy')
local ffi = require('ffi')
-local bit = require('bit')
local prefixes = {}
-- Create subnet prefix rule
local rdlen = #rr.rdata
if rdlen < chunks then return rr end -- Address length mismatch
ffi.copy(addr_buf, rr.rdata, rdlen)
- ffi.copy(addr_buf, prefix[3], chunks)
- -- @todo: CIDR not supported
- to_copy = to_copy - chunks * 8
+ ffi.copy(addr_buf, prefix[3], chunks) -- Rewrite prefix
rr.rdata = ffi.string(addr_buf, rdlen)
return rr
end
- end
+ end
return nil
end
-- Renumber addresses based on config
-local function rule(prefixes)
+local function rule()
return function (state, req)
if state == kres.FAIL then return state end
req = kres.request_t(req)
- pkt = kres.pkt_t(req.answer)
+ local pkt = kres.pkt_t(req.answer)
-- Only successful answers
local records = pkt:section(kres.section.ANSWER)
local ancount = #records
-- Layers
M.layer = {
- finish = rule(prefixes),
+ finish = rule(),
}
return M
-- Signaling Trust Anchor Knowledge in DNS using Key Tag Query
local kres = require('kres')
-local mod = {}
-mod.layer = {}
+local M = {}
+M.layer = {}
-- transform trust anchor keyset structure for one domain name (in wire format)
-- to signalling query name like _ta-keytag1-keytag2.example.com.
local function prepare_query_name(keyset, name)
if not keyset then return nil end
local keytags = {}
- for i, key in ipairs(keyset) do
+ for _, key in ipairs(keyset) do
if key.state == "Valid" then
table.insert(keytags, key.key_tag)
end
table.sort(keytags)
local query = "_ta"
- for i, tag in pairs(keytags) do
+ for _, tag in pairs(keytags) do
query = string.format("%s-%04x", query, tag)
end
if name == "\0" then
end
-- asynchronous query
-- we do not care about result or from where it was obtained
- event.after(0, function (ev)
+ event.after(0, function ()
resolve(qname, kres.type.NULL, kres.class.IN, "NONAUTH")
end)
end
end
-- act on DNSKEY queries which were not answered from cache
-function mod.layer.consume(state, req, pkt)
- local req = kres.request_t(req)
+function M.layer.consume(state, req, _)
+ req = kres.request_t(req)
local qry = req:current()
if qry.stype == kres.type.DNSKEY and not qry.flags.CACHED then
send_ta_query(qry:name())
return state -- do not interfere with normal query processing
end
-return mod
+return M
local M = {}
-local function getLastWord(str)
- local space = 1
- for i=#str, 1, -1 do
- if str:sub(i,i) == " " then
- space = i
- break
- end
- end
- return str:sub(space+1, #str)
-end
-
---Converts string of HEX digits to string
-local function hex2string(hex)
- local str = ""
- for i=1, #hex, 2 do
- local ascii = tonumber(hex:sub(i,i+1), 16)
- str = str .. string.char(ascii)
- end
- return str
-end
-
local function parseCVE(str)
- local first
- local last
- first, last = str:find("CVE")
+ local _, last = str:find("CVE")
local position = last+2
return str:sub(position,-1)
end
-
+
local function parseVersion(str)
local branch = "stable"
- local first
- local last
- first, last = str:find(branch)
+ local _, last = str:find(branch)
local position = last+3
local delimiter = #str
if str:find("|",position) then
end
function M.config(period)
- if period == nil then
+ if period == nil then
print("Expected number of miliseconds. Using default version.config(1*day)")
return
end
print("Expected number of miliseconds. Using default version.config(1*day)")
return
end
- version.period = period
- print(period)
+ M.period = period
if M.ev then event.cancel(M.ev) end
M.ev = event.recurrent(M.period, callhome)
end
function M.init()
- if period == nil then
- M.period = 1*day
- end
+ M.period = M.period or 1 * day
M.ev = event.recurrent(M.period, callhome)
end
}
-- @function View based on TSIG key name.
-function view.tsig(view, tsig, policy)
- view.key[tsig] = policy
+function view.tsig(_, tsig, rules)
+ view.key[tsig] = rules
end
-- @function View based on source IP subnet.
-function view.addr(view, subnet, policy, dst)
+function view.addr(_, subnet, rules, dst)
local subnet_cd = ffi.new('char[16]')
local family = C.kr_straddr_family(subnet)
local bitlen = C.kr_straddr_subnet(subnet_cd, subnet)
- local t = {family, subnet_cd, bitlen, policy}
+ local t = {family, subnet_cd, bitlen, rules}
table.insert(dst and view.dst or view.src, t)
return t
end
end
-- @function Find view for given request
-local function evaluate(view, req)
+local function evaluate(_, req)
local client_key = req.qsource.key
local match_cb = (client_key ~= nil) and view.key[client_key:owner()] or nil
-- Search subnets otherwise
function M.config()
policy.add(policy.suffix(policy.FLAGS('NO_0X20'), {
- -- https://github.com/DNS-OARC/dns-violations/blob/master/2017/DVE-2017-0003.md
+ -- https://github.com/DNS-OARC/dns-violations/blob/master/2017/DVE-2017-0003.md
todname('avqs.mcafee.com'), todname('avts.mcafee.com'),
- -- https://github.com/DNS-OARC/dns-violations/blob/master/2017/DVE-2017-0006.md
- -- Obtained via a reverse search on {ns1,ns3}.panthercdn.com.
+ -- https://github.com/DNS-OARC/dns-violations/blob/master/2017/DVE-2017-0006.md
+ -- Obtained via a reverse search on {ns1,ns3}.panthercdn.com.
todname('cdnga.com'), todname('cdngc.com'), todname('cdngd.com'),
todname('cdngl.com'), todname('cdngm.com'),
todname('cdngc.net'), todname('panthercdn.com'),
-- Just listing the *.in-addr.arpa suffixes would be tedious, as there are many.
M.layer = {
produce = function (state, req)
- local req = kres.request_t(req)
+ req = kres.request_t(req)
local qry = req:current()
if qry.stype ~= kres.type.PTR
or bit.band(state, bit.bor(kres.FAIL, kres.DONE)) ~= 0
-h show this usage help.]],
arg[0]))
return rc
-
end
+
-- Parse CLI arguments
if #arg < 1 then
return help(1)
qtypes = {'A', 'AAAA', 'MX'}
end
-- Assemble config/query
-for i, qtype in ipairs(qtypes) do
+for _, qtype in ipairs(qtypes) do
query = string.format('-t %s -c %s %s', qtype, qclass, qname)
capture = string.format([[
local qname = "%s"