Commit 6fdf1d28 authored by Marek Vavruša's avatar Marek Vavruša

Merge remote-tracking branch 'origin/stubmode'

parents 38be925e 530ed8df
......@@ -88,6 +88,7 @@ struct pkt_rcode {
};
struct query_flag {
static const int NO_MINIMIZE = 1 << 0;
static const int NO_THROTTLE = 1 << 1;
static const int NO_IPV6 = 1 << 2;
static const int NO_IPV4 = 1 << 3;
static const int RESOLVED = 1 << 5;
......@@ -96,6 +97,7 @@ struct query_flag {
static const int NO_CACHE = 1 << 11;
static const int EXPIRING = 1 << 12;
static const int DNSSEC_WANT = 1 << 14;
static const int STUB = 1 << 17;
};
/*
......@@ -242,6 +244,8 @@ struct kr_query *kr_rplan_push(struct kr_rplan *rplan, struct kr_query *parent,
const knot_dname_t *name, uint16_t cls, uint16_t type);
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);
/* Query */
/* Utils */
unsigned kr_rand_uint(unsigned max);
......@@ -331,6 +335,7 @@ ffi.metatype( knot_pkt_t, {
},
})
-- Metatype for query
local ub_t = ffi.typeof('unsigned char *')
local kr_query_t = ffi.typeof('struct kr_query')
ffi.metatype( kr_query_t, {
__index = {
......@@ -345,6 +350,10 @@ ffi.metatype( kr_query_t, {
final = function(qry)
return qry:resolved() and (qry.parent == nil)
end,
nslist = function(qry, ns)
if ns ~= nil then C.kr_nsrep_set(qry, ffi.cast(ub_t, ns), #ns) end
-- @todo: Return list of NS entries, not possible ATM because the NSLIST is union and missing typedef
end,
},
})
-- Metatype for request
......
......@@ -37,7 +37,7 @@ static const knot_dname_t *minimized_qname(struct kr_query *query, uint16_t *qty
{
/* Minimization disabled. */
const knot_dname_t *qname = query->sname;
if (qname[0] == '\0' || query->flags & QUERY_NO_MINIMIZE) {
if (qname[0] == '\0' || query->flags & (QUERY_NO_MINIMIZE|QUERY_STUB)) {
return qname;
}
......@@ -262,6 +262,11 @@ static int process_authority(knot_pkt_t *pkt, struct kr_request *req)
struct kr_query *qry = req->current_query;
const knot_pktsection_t *ns = knot_pkt_section(pkt, KNOT_AUTHORITY);
/* Stub resolution doesn't process authority */
if (qry->flags & QUERY_STUB) {
return KNOT_STATE_CONSUME;
}
#ifdef STRICT_MODE
/* AA, terminate resolution chain. */
if (knot_wire_get_aa(pkt->wire)) {
......@@ -339,7 +344,7 @@ static int process_answer(knot_pkt_t *pkt, struct kr_request *req)
}
/* This answer didn't improve resolution chain, therefore must be authoritative (relaxed to negative). */
if (!is_authoritative(pkt, query)) {
if (!(query->flags & QUERY_STUB) && !is_authoritative(pkt, query)) {
if (pkt_class & (PKT_NXDOMAIN|PKT_NODATA)) {
DEBUG_MSG("<= lame response: non-auth sent negative response\n");
return KNOT_STATE_FAIL;
......@@ -526,8 +531,7 @@ static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt)
}
/* Resolve authority to see if it's referral or authoritative. */
int state = KNOT_STATE_CONSUME;
state = process_authority(pkt, req);
int state = process_authority(pkt, req);
switch(state) {
case KNOT_STATE_CONSUME: /* Not referral, process answer. */
DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??");
......
......@@ -300,8 +300,9 @@ static int validate(knot_layer_t *ctx, knot_pkt_t *pkt)
return ctx->state;
}
/* Pass-through if user doesn't want secure answer. */
if (!(qry->flags & QUERY_DNSSEC_WANT)) {
/* Pass-through if user doesn't want secure answer or stub. */
/* @todo: Validating stub resolver mode. */
if (!(qry->flags & QUERY_DNSSEC_WANT) || (qry->flags & QUERY_STUB)) {
return ctx->state;
}
/* Answer for RRSIG may not set DO=1, but all records MUST still validate. */
......
......@@ -37,20 +37,18 @@
} while (0)
/** Update nameserver representation with current name/address pair. */
static void update_nsrep(struct kr_nsrep *ns, uint8_t *addr, size_t pos)
static void update_nsrep(struct kr_nsrep *ns, size_t pos, uint8_t *addr, size_t addr_len)
{
if (addr == NULL) {
ns->addr[pos].ip.sa_family = AF_UNSPEC;
return;
}
size_t len = pack_obj_len(addr);
void *addr_val = pack_obj_val(addr);
switch(len) {
switch(addr_len) {
case sizeof(struct in_addr):
ADDR_SET(ns->addr[pos].ip4.sin, AF_INET, addr_val, len); break;
ADDR_SET(ns->addr[pos].ip4.sin, AF_INET, addr, addr_len); break;
case sizeof(struct in6_addr):
ADDR_SET(ns->addr[pos].ip6.sin6, AF_INET6, addr_val, len); break;
ADDR_SET(ns->addr[pos].ip6.sin6, AF_INET6, addr, addr_len); break;
default: assert(0); break;
}
}
......@@ -60,7 +58,13 @@ static void update_nsrep_set(struct kr_nsrep *ns, const knot_dname_t *name, uint
ns->name = name;
ns->score = score;
for (size_t i = 0; i < KR_NSREP_MAXADDR; ++i) {
update_nsrep(ns, addr[i], i);
if (addr[i]) {
void *addr_val = pack_obj_val(addr[i]);
size_t len = pack_obj_len(addr[i]);
update_nsrep(ns, i, addr_val, len);
} else {
update_nsrep(ns, i, NULL, 0);
}
}
}
......@@ -157,6 +161,19 @@ static int eval_nsrep(const char *k, void *v, void *baton)
return kr_ok();
}
int kr_nsrep_set(struct kr_query *qry, uint8_t *addr, size_t addr_len)
{
if (!qry || !addr) {
return kr_error(EINVAL);
}
qry->ns.name = (const uint8_t *)"";
qry->ns.score = KR_NS_UNKNOWN;
qry->ns.reputation = 0;
update_nsrep(&qry->ns, 0, addr, addr_len);
update_nsrep(&qry->ns, 1, NULL, 0);
return kr_ok();
}
#define ELECT_INIT(ns, ctx_) do { \
(ns)->ctx = (ctx_); \
(ns)->addr[0].ip.sa_family = AF_UNSPEC; \
......
......@@ -80,6 +80,15 @@ struct kr_nsrep
#define kr_nsrep_inaddr_len(addr) \
((addr).ip.sa_family == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr))
/**
* Set given NS address.
* @param qry updated query
* @param addr address bytes (struct in_addr or struct in6_addr)
* @param addr_len address bytes length (type will be derived from this)
* @return 0 or an error code
*/
int kr_nsrep_set(struct kr_query *qry, uint8_t *addr, size_t addr_len);
/**
* Elect best nameserver/address pair from the nsset.
* @param qry updated query
......
......@@ -325,8 +325,14 @@ static int query_finalize(struct kr_request *request, struct kr_query *qry, knot
knot_pkt_begin(pkt, KNOT_ADDITIONAL);
if (!(qry->flags & QUERY_SAFEMODE)) {
ret = edns_create(pkt, request->answer, request);
if (ret == 0) { /* Enable DNSSEC for query. */
if (qry->flags & QUERY_DNSSEC_WANT) {
if (ret == 0) {
/* Stub resolution (ask for +rd and +do) */
if (qry->flags & QUERY_STUB) {
knot_wire_set_rd(pkt->wire);
if (knot_pkt_has_dnssec(request->answer))
knot_edns_set_do(pkt->opt_rr);
/* Full resolution (ask for +cd and +do) */
} else if (qry->flags & QUERY_DNSSEC_WANT) {
knot_edns_set_do(pkt->opt_rr);
knot_wire_set_cd(pkt->wire);
}
......@@ -538,6 +544,11 @@ static int zone_cut_check(struct kr_request *request, struct kr_query *qry, knot
map_t *trust_anchors = &request->ctx->trust_anchors;
map_t *negative_anchors = &request->ctx->negative_anchors;
/* Stub mode, just forward and do not solve cut. */
if (qry->flags & QUERY_STUB) {
return KNOT_STATE_PRODUCE;
}
/* The query wasn't resolved from cache,
* now it's the time to look up closest zone cut from cache. */
if (qry->flags & QUERY_AWAIT_CUT) {
......@@ -647,7 +658,7 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t
}
if (qry->flags & (QUERY_AWAIT_IPV4|QUERY_AWAIT_IPV6)) {
kr_nsrep_elect_addr(qry, request->ctx);
} else if (!qry->ns.name || !(qry->flags & QUERY_TCP)) { /* Keep address when TCP retransmit. */
} else if (!qry->ns.name || !(qry->flags & (QUERY_TCP|QUERY_STUB))) { /* Keep NS when requerying/stub. */
/* Root DNSKEY must be fetched from the hints to avoid chicken and egg problem. */
if (qry->sname[0] == '\0' && qry->stype == KNOT_RRTYPE_DNSKEY) {
kr_zonecut_set_sbelt(request->ctx, &qry->zone_cut);
......
......@@ -44,6 +44,7 @@
X(DNSSEC_WANT, 1 << 14) /**< Want DNSSEC secured answer. */ \
X(DNSSEC_BOGUS, 1 << 15) /**< Query response is DNSSEC bogus. */ \
X(DNSSEC_INSECURE, 1 << 16) /**< Query response is DNSSEC insecure. */ \
X(STUB, 1 << 17) /**< Stub resolution, accept received answer as solved. */ \
/** Query flags */
enum kr_query_flag {
......
......@@ -217,7 +217,11 @@ int kr_straddr_family(const char *addr)
int kr_family_len(int family)
{
return (family == AF_INET) ? sizeof(struct in_addr) : sizeof(struct in6_addr);
switch (family) {
case AF_INET: return sizeof(struct in_addr);
case AF_INET6: return sizeof(struct in6_addr);
default: return kr_error(EINVAL);
}
}
int kr_straddr_subnet(void *dst, const char *addr)
......
......@@ -24,6 +24,7 @@ There are several defined actions:
* ``DENY`` - return NXDOMAIN answer
* ``DROP`` - terminate query resolution, returns SERVFAIL to requestor
* ``TC`` - set TC=1 if the request came through UDP, forcing client to retry with TCP
* ``FORWARD(ip)`` - forward query to given IP and proxy back response (stub mode)
.. note:: The module (and ``kres``) treats domain names as wire, not textual representation. So each label in name is prefixed with its length, e.g. "example.com" equals to ``"\7example\3com"``.
......@@ -52,14 +53,21 @@ Example configuration
end)
-- Enforce local RPZ
policy:add(policy.rpz(policy.DENY, 'blacklist.rpz'))
-- Forward all queries below 'company.se' to given resolver
policy:add(policy.suffix(policy.FORWARD('192.168.1.1'), {'\7company\2se'}))
-- Forward all queries matching pattern
policy:add(policy.pattern(policy.FORWARD('2001:DB8::1'), '\4bad[0-9]\2cz'))
-- Forward all queries (complete stub mode)
policy:add(policy.all(policy.FORWARD('2001:DB8::1')))
Properties
^^^^^^^^^^
.. envvar:: policy.PASS (number)
.. envvar:: policy.DENY (number)
.. envvar:: policy.DROP (number)
.. envvar:: policy.TC (number)
.. envvar:: policy.PASS (number)
.. envvar:: policy.DENY (number)
.. envvar:: policy.DROP (number)
.. envvar:: policy.TC (number)
.. envvar:: policy.FORWARD (function)
.. function:: policy:add(rule)
......@@ -68,6 +76,12 @@ Properties
Policy to block queries based on the QNAME regex matching.
.. function:: policy.all(action)
:param action: executed action for all queries
Perform action for all queries (no filtering).
.. function:: policy.pattern(action, pattern)
:param action: action if the pattern matches QNAME
......
local kres = require('kres')
-- Forward request, and solve as stub query
local function forward(target)
local dst_ip = kres.str2ip(target)
if dst_ip == nil then error("FORWARD target '"..target..'" is not a valid IP address') end
return function(state, req)
req = kres.request_t(req)
local qry = req:current()
qry.flags = qry.flags + kres.query.STUB
qry:nslist(dst_ip)
return state
end
end
local policy = {
-- Policies
PASS = 1, DENY = 2, DROP = 3, TC = 4,
PASS = 1, DENY = 2, DROP = 3, TC = 4, FORWARD = forward,
-- Special values
ANY = 0,
}
-- All requests
function policy.all(action)
return function(req, query) return action end
end
-- Requests which QNAME matches given zone list (i.e. suffix match)
function policy.suffix(action, zone_list)
local AC = require('aho-corasick')
......@@ -127,6 +146,8 @@ function policy.enforce(state, req, action)
answer:tc(1) -- ^ Only UDP queries
return kres.DONE
end
elseif type(action) == 'function' then
return action(state, req)
end
return state
end
......
......@@ -38,6 +38,8 @@ Example configuration
view:addr('10.0.0.0/8', policy.suffix(policy.DROP, {'\3xxx'}))
-- RPZ for subset of clients
view:addr('192.168.1.0/24', policy.rpz(policy.PASS, 'whitelist.rpz'))
-- Forward all queries from given subnet to proxy
view:addr('10.0.0.0/8', policy.all(policy.FORWARD('2001:DB8::1')))
Properties
^^^^^^^^^^
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment