Commit e2c6ec7d authored by Jan Včelák's avatar Jan Včelák 🚀

Merge branch 'additional_fix' into 'master'

internet: add glue for delegated servers if authoritative answer

See merge request !588
parents 9d5efcb5 42623b07
......@@ -30,12 +30,6 @@
#include "contrib/mempattern.h"
#include "contrib/sockaddr.h"
/*! \brief Kind of additional record. */
enum additional_kind {
ADDITIONAL_OPTIONAL = 0,
ADDITIONAL_MANDATORY,
};
/*! \brief Check if given node was already visited. */
static int wildcard_has_visited(struct query_data *qdata, const zone_node_t *node)
{
......@@ -281,47 +275,40 @@ static int put_delegation(knot_pkt_t *pkt, struct query_data *qdata)
/*! \brief Put additional records for given RR. */
static int put_additional(knot_pkt_t *pkt, const knot_rrset_t *rr,
struct query_data *qdata, knot_rrinfo_t *info,
int state, enum additional_kind kind)
struct query_data *qdata, knot_rrinfo_t *info, int state)
{
if (rr->additional == NULL) {
return KNOT_EOK;
}
/* Valid types for ADDITIONALS insertion. */
/* \note Not resolving CNAMEs as MX/NS name must not be an alias. (RFC2181/10.3) */
static const uint16_t ar_type_list[] = {KNOT_RRTYPE_A, KNOT_RRTYPE_AAAA};
static const uint16_t ar_type_list[] = { KNOT_RRTYPE_A, KNOT_RRTYPE_AAAA };
static const int ar_type_count = 2;
int ret = KNOT_EOK;
/* All RRs should have additional node cached or NULL. */
for (uint16_t i = 0; i < rr->rrs.rr_count; i++) {
const zone_node_t *node = rr->additional[i];
if (node == NULL) {
continue;
}
additional_t *additional = (additional_t *)rr->additional;
bool is_notauth = (node->flags & (NODE_FLAGS_DELEG | NODE_FLAGS_NONAUTH));
bool is_glue = is_notauth &&
state == DELEG && rr->type == KNOT_RRTYPE_NS &&
knot_dname_in(rr->owner, node->owner);
/* Iterate over the additionals. */
for (uint16_t i = 0; i < additional->count; i++) {
glue_t *glue = &additional->glues[i];
uint32_t flags = KNOT_PF_NULL;
/* Non-authoritative node allowed only as a glue. */
if (is_notauth && !is_glue) {
continue;
/* Optional glue doesn't cause truncation. (RFC 1034/4.3.2 step 3b). */
if (state != DELEG || glue->optional) {
flags |= KNOT_PF_NOTRUNC;
}
/* Glue is required as per RFC 1034 Section 4.3.2 step 3b. */
if (kind != (is_glue ? ADDITIONAL_MANDATORY : ADDITIONAL_OPTIONAL)) {
continue;
}
uint32_t flags = KNOT_PF_CHECKDUP | (is_glue ? 0 : KNOT_PF_NOTRUNC);
uint16_t hint = knot_pkt_compr_hint(info, KNOT_COMPR_HINT_RDATA + i);
knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
uint16_t hint = knot_pkt_compr_hint(info, KNOT_COMPR_HINT_RDATA +
glue->ns_pos);
knot_rrset_t rrsigs = node_rrset(glue->node, KNOT_RRTYPE_RRSIG);
for (int k = 0; k < ar_type_count; ++k) {
knot_rrset_t additional = node_rrset(node, ar_type_list[k]);
if (knot_rrset_empty(&additional)) {
knot_rrset_t rrset = node_rrset(glue->node, ar_type_list[k]);
if (knot_rrset_empty(&rrset)) {
continue;
}
ret = ns_put_rr(pkt, &additional, &rrsigs, hint, flags, qdata);
ret = ns_put_rr(pkt, &rrset, &rrsigs, hint, flags, qdata);
if (ret != KNOT_EOK) {
break;
}
......@@ -608,47 +595,28 @@ static int solve_authority_dnssec(int state, knot_pkt_t *pkt, struct query_data
}
}
static int solve_additional_kind(int state, knot_pkt_t *pkt, struct query_data *qdata,
enum additional_kind kind)
static int solve_additional(int state, knot_pkt_t *pkt, struct query_data *qdata,
void *ctx)
{
int ret = KNOT_EOK;
/* Only glue can be mandatory. */
if (kind == ADDITIONAL_MANDATORY && state != DELEG) {
return ret;
}
/* Scan all RRs in ANSWER/AUTHORITY. */
for (uint16_t i = 0; i < pkt->rrset_count; ++i) {
knot_rrset_t *rr = &pkt->rr[i];
knot_rrinfo_t *info = &pkt->rr_info[i];
/* Skip types for which it doesn't apply. */
if (!knot_rrtype_additional_needed(pkt->rr[i].type)) {
if (!knot_rrtype_additional_needed(rr->type)) {
continue;
}
/* Put additional records for given type. */
ret = put_additional(pkt, rr, qdata, info, state, kind);
ret = put_additional(pkt, rr, qdata, info, state);
if (ret != KNOT_EOK) {
break;
}
}
return ret;
}
static int solve_additional(int state, knot_pkt_t *pkt,
struct query_data *qdata, void *ctx)
{
int ret = KNOT_EOK;
/* First mandatory, then optional. */
ret = solve_additional_kind(state, pkt, qdata, ADDITIONAL_MANDATORY);
if (ret == KNOT_EOK) {
ret = solve_additional_kind(state, pkt, qdata, ADDITIONAL_OPTIONAL);
}
/* Evaluate final state. */
switch (ret) {
case KNOT_EOK: return state; /* Keep current state. */
......
......@@ -51,10 +51,8 @@ static int free_additional(zone_node_t **node, void *data)
for (uint16_t i = 0; i < (*node)->rrset_count; ++i) {
struct rr_data *data = &(*node)->rrs[i];
if (data->additional) {
free(data->additional);
data->additional = NULL;
}
additional_clear(data->additional);
data->additional = NULL;
}
return KNOT_EOK;
......
......@@ -114,22 +114,23 @@ static int create_nsec3_name(const zone_contents_t *zone,
}
/*! \brief Link pointers to additional nodes for this RRSet. */
static int discover_additionals(struct rr_data *rr_data, zone_contents_t *zone)
static int discover_additionals(const knot_dname_t *owner, struct rr_data *rr_data,
zone_contents_t *zone)
{
assert(rr_data != NULL);
const knot_rdataset_t *rrs = &rr_data->rrs;
/* Drop possible previous additional nodes. */
additional_clear(rr_data->additional);
/* Create new additional nodes. */
const knot_rdataset_t *rrs = &rr_data->rrs;
uint16_t rdcount = rrs->rr_count;
if (rr_data->additional) {
free(rr_data->additional);
}
rr_data->additional = malloc(rdcount * sizeof(zone_node_t *));
if (rr_data->additional == NULL) {
return KNOT_ENOMEM;
}
uint16_t mandatory_count = 0;
uint16_t others_count = 0;
glue_t mandatory[rdcount];
glue_t others[rdcount];
/* Scan new additional nodes. */
for (uint16_t i = 0; i < rdcount; i++) {
const knot_dname_t *dname = knot_rdata_name(rrs, i, rr_data->type);
const zone_node_t *node = NULL, *encloser = NULL, *prev = NULL;
......@@ -143,7 +144,44 @@ static int discover_additionals(struct rr_data *rr_data, zone_contents_t *zone)
assert(node != NULL);
}
rr_data->additional[i] = (zone_node_t *)node;
if (node == NULL) {
continue;
}
glue_t *glue;
if ((node->flags & (NODE_FLAGS_DELEG | NODE_FLAGS_NONAUTH)) &&
rr_data->type == KNOT_RRTYPE_NS &&
knot_dname_in(owner, node->owner)) {
glue = &mandatory[mandatory_count++];
glue->optional = false;
} else {
glue = &others[others_count++];
glue->optional = true;
}
glue->node = node;
glue->ns_pos = i;
}
/* Store sorted additionals by the type, mandatory first. */
size_t total_count = mandatory_count + others_count;
if (total_count > 0) {
rr_data->additional = malloc(sizeof(additional_t));
if (rr_data->additional == NULL) {
return KNOT_ENOMEM;
}
rr_data->additional->count = total_count;
size_t size = total_count * sizeof(glue_t);
rr_data->additional->glues = malloc(size);
if (rr_data->additional->glues == NULL) {
free(rr_data->additional);
return KNOT_ENOMEM;
}
size_t mandatory_size = mandatory_count * sizeof(glue_t);
memcpy(rr_data->additional->glues, mandatory, mandatory_size);
memcpy(rr_data->additional->glues + mandatory_count, others,
size - mandatory_size);
}
return KNOT_EOK;
......@@ -305,7 +343,7 @@ static int adjust_additional(zone_node_t **tnode, void *data)
for(uint16_t i = 0; i < node->rrset_count; ++i) {
struct rr_data *rr_data = &node->rrs[i];
if (knot_rrtype_additional_needed(rr_data->type)) {
int ret = discover_additionals(rr_data, args->zone);
int ret = discover_additionals(node->owner, rr_data, args->zone);
if (ret != KNOT_EOK) {
return ret;
}
......
/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,11 +19,21 @@
#include "libknot/rrtype/rrsig.h"
#include "contrib/mempattern.h"
void additional_clear(additional_t *additional)
{
if (additional == NULL) {
return;
}
free(additional->glues);
free(additional);
}
/*! \brief Clears allocated data in RRSet entry. */
static void rr_data_clear(struct rr_data *data, knot_mm_t *mm)
{
knot_rdataset_clear(&data->rrs, mm);
free(data->additional);
additional_clear(data->additional);
}
/*! \brief Clears allocated data in RRSet entry. */
......
/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -54,11 +54,24 @@ typedef struct zone_node {
uint8_t flags; /*!< \ref node_flags enum. */
} zone_node_t;
/*!< \brief Glue node context. */
typedef struct {
const zone_node_t *node; /*!< Glue node. */
uint16_t ns_pos; /*!< Corresponding NS record position (for compression). */
bool optional; /*!< Optional glue indicator. */
} glue_t;
/*!< \brief Additional data. */
typedef struct {
glue_t *glues; /*!< Glue data. */
uint16_t count; /*!< Number of glue nodes. */
} additional_t;
/*!< \brief Structure storing RR data. */
struct rr_data {
uint16_t type; /*!< \brief RR type of data. */
knot_rdataset_t rrs; /*!< \brief Data of given type. */
zone_node_t **additional; /*!< \brief Additional nodes with glues. */
uint16_t type; /*!< RR type of data. */
knot_rdataset_t rrs; /*!< Data of given type. */
additional_t *additional; /*!< Additional nodes with glues. */
};
/*! \brief Flags used to mark nodes with some property. */
......@@ -77,6 +90,13 @@ enum node_flags {
NODE_FLAGS_WILDCARD_CHILD = 1 << 4
};
/*!
* \brief Clears additional structure.
*
* \param additional Additional to clear.
*/
void additional_clear(additional_t *additional);
/*!
* \brief Creates and initializes new node structure.
*
......
......@@ -42,7 +42,7 @@ struct knot_rrset {
uint16_t rclass; /*!< CLASS of the RRSet. */
knot_rdataset_t rrs; /*!< RRSet's RRs */
/* Optional fields. */
struct zone_node **additional; /*!< Additional records. */
void *additional; /*!< Additional records. */
};
typedef struct knot_rrset knot_rrset_t;
......
......@@ -2,10 +2,19 @@ $ORIGIN tc.test.
$TTL 60
@ SOA @ admin 1 60 60 1800 60
; local name servers
NS @
A 0.0.0.0
NS a1234567890123456789012345678901234567890123456789.1234567890123456789012345678901234567890123456789.1234567890123456789012345678901234567890123456789.1234567890123456789012345678901234567890123456789.1234567890123456789012345678901234567890a
NS b1234567890123456789012345678901234567890123456789.1234567890123456789012345678901234567890123456789.1234567890123456789012345678901234567890123456789.1234567890123456789012345678901234567890123456789.1234567890123456789012345678901234567890b
AAAA ::
; foreign name server
NS ns.glue.deleg
ns.glue.deleg AAAA ::
glue.deleg DS 0 0 0 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
glue.deleg NS unreachable.glue.deleg
; delegation with glue
glue DS 0 0 0 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
glue NS ns1.glue
......
......@@ -13,19 +13,21 @@ t.link(zone, knot)
t.start()
class DelegationTest:
def __init__(self, name):
def __init__(self, name, authoritative=False):
self._name = name
self._auth = authoritative
def _get_flags(self, truncated):
if truncated:
return ("TC", "AA")
if self._auth:
return ("AA TC", "") if truncated else ("AA", "TC")
else:
return ("", "AA TC")
return ("TC", "AA") if truncated else ("", "AA TC")
def run(self, bufsize=None, truncated=False, counts=None):
name = "www.%s" % self._name
name = "%s%s" % ("" if self._auth else "www.", self._name)
rtype = "NS" if self._auth else "A"
flags, noflags = self._get_flags(truncated)
resp = knot.dig(name, "A", udp=True, dnssec=True, bufsize=bufsize)
resp = knot.dig(name, rtype, udp=True, dnssec=True, bufsize=bufsize)
resp.check(rcode="NOERROR", noflags=noflags, flags=flags)
for section in counts:
for rtype in counts[section]:
......@@ -37,6 +39,35 @@ class DelegationTest:
def __exit__(self, exc_type, exc_vaue, traceback):
return False
# Authoritative answer with glue of foreign name server
with DelegationTest("tc.test", authoritative=True) as test:
# incomplete answer, no signature
test.run(bufsize=592, truncated=True, counts={
"answer": {"NS": 4, "RRSIG": 0},
"additional": {"AAAA": 0, "RRSIG": 0}}
)
# complete answer, no additionals
test.run(bufsize=695, truncated=False, counts={
"answer": {"NS": 4, "RRSIG": 1},
"additional": {"AAAA": 0, "RRSIG": 0}}
)
# complete answer, one optional additional for foreign name server
test.run(bufsize=723, truncated=False, counts={
"answer": {"NS": 4, "RRSIG": 1},
"additional": {"AAAA": 1, "RRSIG": 0}}
)
# complete answer, all optional additionals
test.run(bufsize=751, truncated=False, counts={
"answer": {"NS": 4, "RRSIG": 1},
"additional": {"AAAA": 2, "RRSIG": 0}}
)
# complete answer, all optional additionals with signature
test.run(bufsize=2000, truncated=False, counts={
"answer": {"NS": 4, "RRSIG": 1},
"additional": {"AAAA": 2, "RRSIG": 1}}
)
# Delegation with glue
with DelegationTest("glue.tc.test") as test:
......@@ -61,7 +92,7 @@ with DelegationTest("glue.tc.test") as test:
"additional": {"AAAA": 1, "RRSIG": 0}}
)
# complete delegation, complete glue
test.run(bufsize=768, truncated=False, counts={
test.run(bufsize=2000, truncated=False, counts={
"authority": {"NS": 2, "DS": 1, "RRSIG": 1},
"additional": {"AAAA": 2, "RRSIG": 0}}
)
......@@ -80,10 +111,11 @@ with DelegationTest("unreachable.tc.test") as test:
"additional": {"AAAA": 0, "RRSIG": 0}}
)
# complete delegation, no glue available
test.run(bufsize=719, truncated=False, counts={
test.run(bufsize=2000, truncated=False, counts={
"authority": {"NS": 2, "DS": 1, "RRSIG": 1},
"additional": {"AAAA": 0, "RRSIG": 0}}
)
# Delegation with foreign name servers
with DelegationTest("foreign.tc.test") as test:
......@@ -98,7 +130,7 @@ with DelegationTest("foreign.tc.test") as test:
"additional": {"AAAA": 0, "RRSIG": 0}}
)
# complete delegation, no glue needed
test.run(bufsize=722, truncated=False, counts={
test.run(bufsize=2000, truncated=False, counts={
"authority": {"NS": 2, "DS": 1, "RRSIG": 1},
"additional": {"AAAA": 0, "RRSIG": 0}}
)
......@@ -137,7 +169,7 @@ with DelegationTest("parent.tc.test") as test:
"additional": {"AAAA": 2, "RRSIG": 1}}
)
# complete delegation, all optional additionals with signatures
test.run(bufsize=976, truncated=False, counts={
test.run(bufsize=2000, truncated=False, counts={
"authority": {"NS": 2, "DS": 1, "RRSIG": 1},
"additional": {"AAAA": 2, "RRSIG": 2}}
)
......@@ -176,9 +208,14 @@ with DelegationTest("mixed.tc.test") as test:
"additional": {"AAAA": 3, "RRSIG": 0}}
)
# complete delegation, full glue, optional
test.run(bufsize=999, truncated=False, counts={
test.run(bufsize=924, truncated=False, counts={
"authority": {"NS": 6, "DS": 1, "RRSIG": 1},
"additional": {"AAAA": 4, "RRSIG": 0}}
)
# complete delegation, full glue, optional with signature
test.run(bufsize=2000, truncated=False, counts={
"authority": {"NS": 6, "DS": 1, "RRSIG": 1},
"additional": {"AAAA": 3, "RRSIG": 1}}
"additional": {"AAAA": 4, "RRSIG": 1}}
)
t.stop()
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