struct kr_query *kr_rplan_resolved(struct kr_rplan *rplan);
struct kr_query *kr_rplan_next(struct kr_query *qry);
/* Nameservers */
-int kr_nsrep_set(struct kr_query *qry, uint8_t *addr, size_t addr_len, int port);
+int kr_nsrep_set(struct kr_query *qry, size_t index, uint8_t *addr, size_t addr_len, int port);
/* Query */
/* Utils */
unsigned kr_rand_uint(unsigned max);
final = function(qry)
return qry:resolved() and (qry.parent == nil)
end,
- nslist = function(qry, ns, port)
- if ns ~= nil then C.kr_nsrep_set(qry, ffi.cast(ub_t, ns), #ns, port) end
- -- @todo: Return list of NS entries, not possible ATM because the NSLIST is union and missing typedef
+ nslist = function(qry, list)
+ assert(#list <= 4, 'maximum of 4 addresses can be evaluated for each query')
+ for i, ns in ipairs(list) do
+ C.kr_nsrep_set(qry, i - 1, ffi.cast(ub_t, ns[1]), #ns[1], ns[2] or 53)
+ end
+ -- If less than maximum NSs, insert guard to terminate the list
+ if #list < 4 then
+ C.kr_nsrep_set(qry, #list, nil, 0, 0)
+ end
end,
},
})
break; /* OK */
case KNOT_RCODE_REFUSED:
case KNOT_RCODE_SERVFAIL: {
- DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??");
if (query->flags & QUERY_STUB) { break; } /* Pass through in stub mode */
+ DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??");
query->fails += 1;
if (query->fails >= KR_QUERY_NSRETRY_LIMIT) {
query->fails = 0; /* Reset per-query counter. */
return kr_ok();
}
-int kr_nsrep_set(struct kr_query *qry, uint8_t *addr, size_t addr_len, int port)
+int kr_nsrep_set(struct kr_query *qry, size_t index, uint8_t *addr, size_t addr_len, int port)
{
- if (!qry || !addr) {
+ if (!qry) {
return kr_error(EINVAL);
}
+ if (index >= KR_NSREP_MAXADDR) {
+ return kr_error(ENOSPC);
+ }
qry->ns.name = (const uint8_t *)"";
- qry->ns.score = KR_NS_UNKNOWN;
- qry->ns.reputation = 0;
- update_nsrep(&qry->ns, 0, addr, addr_len, port);
- update_nsrep(&qry->ns, 1, NULL, 0, 0);
+ /* Reset score on first entry */
+ if (index == 0) {
+ qry->ns.score = KR_NS_UNKNOWN;
+ qry->ns.reputation = 0;
+ }
+ /* Retrieve RTT from cache */
+ if (addr && addr_len > 0) {
+ struct kr_context *ctx = qry->ns.ctx;
+ unsigned *score = ctx ? lru_get(ctx->cache_rtt, (const char *)addr, addr_len) : NULL;
+ if (score) {
+ qry->ns.score = MIN(qry->ns.score, *score);
+ }
+ }
+ update_nsrep(&qry->ns, index, addr, addr_len, port);
return kr_ok();
}
/**
* Set given NS address.
* @param qry updated query
+ * @param index index of the updated target
* @param addr address bytes (struct in_addr or struct in6_addr)
* @param addr_len address bytes length (type will be derived from this)
* @param port address port (if <= 0, 53 will be used)
* @return 0 or an error code
*/
KR_EXPORT
-int kr_nsrep_set(struct kr_query *qry, uint8_t *addr, size_t addr_len, int port);
+int kr_nsrep_set(struct kr_query *qry, size_t index, uint8_t *addr, size_t addr_len, int port);
/**
* Elect best nameserver/address pair from the nsset.
if (addr->sa_family == AF_UNSPEC) {
break;
}
+ if (!kr_inaddr_equal(dst, addr)) {
+ continue;
+ }
inet_ntop(addr->sa_family, kr_nsrep_inaddr(qry->ns.addr[i]), ns_str, sizeof(ns_str));
- DEBUG_MSG(qry, "%s%s: '%s' score: %u zone cut: '%s' m12n: '%s' type: '%s' proto: '%s'\n",
- i == 0 ? "=>" : " ",
- kr_inaddr_equal(dst, addr) ? "*querying" : " optional",
- ns_str, qry->ns.score, zonecut_str, qname_str, type_str, (qry->flags & QUERY_TCP)?"tcp":"udp");
- }
- }
+ DEBUG_MSG(qry, "=> querying: '%s' score: %u zone cut: '%s' m12n: '%s' type: '%s' proto: '%s'\n",
+ ns_str, qry->ns.score, zonecut_str, qname_str, type_str, (qry->flags & QUERY_TCP) ? "tcp" : "udp");
+ break;
+ }}
return kr_ok();
}
/* Class and type must be set outside this function. */
qry->flags = rplan->request->options;
qry->parent = parent;
+ qry->ns.ctx = rplan->request->ctx;
qry->ns.addr[0].ip.sa_family = AF_UNSPEC;
gettimeofday(&qry->timestamp, NULL);
kr_zonecut_init(&qry->zone_cut, (const uint8_t *)"", rplan->pool);
daf.add 'src = 127.0.0.0/8 rewrite example.com A 127.0.0.2'
-- Mirror queries matching given name to DNS logger
- daf.add 'qname ~ %w+.example.com MIRROR 127.0.0.2'
- daf.add 'qname ~ example-%d.com MIRROR 127.0.0.3@5353'
+ daf.add 'qname ~ %w+.example.com mirror 127.0.0.2'
+ daf.add 'qname ~ example-%d.com mirror 127.0.0.3@5353'
-- Forward queries from subnet
daf.add 'src = 127.0.0.1/8 forward 127.0.0.1@5353'
+ -- Forward to multiple targets
+ daf.add 'src = 127.0.0.1/8 forward 127.0.0.1@5353,127.0.0.2@5353'
-- Truncate queries based on destination IPs
daf.add 'dst = 192.0.2.51 truncate'
local actions = {
pass = 1, deny = 2, drop = 3, tc = 4, truncate = 4,
forward = function (g)
- return policy.FORWARD(g())
+ local addrs = {}
+ local tok = g()
+ for addr in string.gmatch(tok, '[^,]+') do
+ table.insert(addrs, addr)
+ end
+ return policy.FORWARD(addrs)
end,
mirror = function (g)
return policy.MIRROR(g())
socket_client = function () return error("missing ffi library, required for this policy") end
end
+local function parse_target(target)
+ local addr, port = target:match '([^@]*)@?(.*)'
+ port = port and tonumber(port) or 53
+ addr = kres.str2ip(addr)
+ if addr == nil then
+ error("target '"..target..'" is not a valid IP address')
+ end
+ return addr, port
+end
+
-- Mirror request elsewhere, and continue solving
local function mirror(target)
- local addr, port = target:match '([^@]*)@?(.*)'
- if not port or #port == 0 then port = 53 end
+ local addr, port = parse_target(target)
local sink, err = socket_client(addr, port)
if not sink then panic('MIRROR target %s is not a valid: %s', target, err) end
return function(state, req)
-- Forward request, and solve as stub query
local function forward(target)
- local addr, port = target:match '([^@]*)@?(.*)'
- port = port and tonumber(port) or 53
- addr = kres.str2ip(addr)
- if addr == nil then error("FORWARD target '"..target..'" is not a valid IP address') end
+ local list = {}
+ if type(target) == 'table' then
+ for _, v in pairs(target) do
+ table.insert(list, {parse_target(v)})
+ assert(#list <= 4, 'at most 4 FORWARD targets are supported')
+ end
+ else
+ table.insert(list, {parse_target(target)})
+ end
return function(state, req)
req = kres.request_t(req)
local qry = req:current()
-- Switch mode to stub resolver, do not track origin zone cut since it's not real authority NS
qry.flags = bit.band(bit.bor(qry.flags, kres.query.STUB), bit.bnot(kres.query.ALWAYS_CUT))
- qry:nslist(addr, port)
+ qry:nslist(list)
return state
end
end
table.insert(prefixes, names and ren.name(from, to) or ren.prefix(from, to))
end
-- Return rule closure
- tbl = nil
return ren.rule(prefixes)
end
local function rpz_parse(action, path)
local rules = {}
- local ffi = require('ffi')
local action_map = {
-- RPZ Policy Actions
['\0'] = action,