Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Commits on Source (9)
......@@ -6,6 +6,7 @@ Bugfixes
- modules/dns64: fix incorrect packet writes for cached packets (#727, !1275)
- xdp: make it work also with libknot 3.1 (#735, !1276)
- prefill module: fix lockup when starting multiple idle instances (!1285)
- validator: fix some failing negative NSEC proofs (!1294, #738, #443)
Knot Resolver 5.5.0 (2022-03-15)
......
......@@ -970,6 +970,7 @@ local function rank_tostring(rank)
table.insert(names, string.lower(name))
end
end
table.sort(names) -- pairs() above doesn't give a stable ordering
return string.format('0%.2o (%s)', rank, table.concat(names, ' '))
end
......
......@@ -16,11 +16,11 @@
#include "lib/defines.h"
#include "lib/dnssec/nsec.h"
#include "lib/utils.h"
#include "resolve.h"
int kr_nsec_children_in_zone_check(const uint8_t *bm, uint16_t bm_size)
{
if (!bm)
if (kr_fails_assert(bm))
return kr_error(EINVAL);
const bool parent_side =
dnssec_nsec_bitmap_contains(bm, bm_size, KNOT_RRTYPE_DNAME)
......@@ -94,26 +94,28 @@ static int dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2)
/**
* Check whether the NSEC RR proves that there is no closer match for <SNAME, SCLASS>.
* Check whether this nsec proves that there is no closer match for sname.
*
* @param nsec NSEC RRSet.
* @param sname Searched name.
* @return 0 if proves, >0 if not (abs(ENOENT)), or error code (<0).
* @return 0 if proves, >0 if not (abs(ENOENT) or abs(EEXIST)), or error code (<0).
*/
static int nsec_covers(const knot_rrset_t *nsec, const knot_dname_t *sname)
{
if (kr_fails_assert(nsec && sname))
return kr_error(EINVAL);
if (dname_cmp(sname, nsec->owner) <= 0)
return abs(ENOENT); /* 'sname' before 'owner', so can't be covered */
const int cmp = dname_cmp(sname, nsec->owner);
if (cmp < 0) return abs(ENOENT); /* 'sname' before 'owner', so can't be covered */
if (cmp == 0) return abs(EEXIST); /* matched, not covered */
/* If NSEC 'owner' >= 'next', it means that there is nothing after 'owner' */
/* We have to lower-case it with libknot >= 2.7; see also RFC 6840 5.1. */
/* We have to lower-case 'next' with libknot >= 2.7; see also RFC 6840 5.1. */
knot_dname_t next[KNOT_DNAME_MAXLEN];
int ret = knot_dname_to_wire(next, knot_nsec_next(nsec->rrs.rdata), sizeof(next));
if (kr_fails_assert(ret >= 0))
return kr_error(ret);
knot_dname_to_lower(next);
/* If NSEC 'owner' >= 'next', it means that there is nothing after 'owner' */
const bool is_last_nsec = dname_cmp(nsec->owner, next) >= 0;
const bool in_range = is_last_nsec || dname_cmp(sname, next) < 0;
if (!in_range)
......@@ -130,140 +132,6 @@ static int nsec_covers(const knot_rrset_t *nsec, const knot_dname_t *sname)
return kr_nsec_children_in_zone_check(bm, bm_size);
}
#define FLG_NOEXIST_RRTYPE (1 << 0) /**< <SNAME, SCLASS> exists, <SNAME, SCLASS, STYPE> does not exist. */
#define FLG_NOEXIST_RRSET (1 << 1) /**< <SNAME, SCLASS> does not exist. */
#define FLG_NOEXIST_WILDCARD (1 << 2) /**< No wildcard covering <SNAME, SCLASS> exists. */
#define FLG_NOEXIST_CLOSER (1 << 3) /**< Wildcard covering <SNAME, SCLASS> exists, but doesn't match STYPE. */
/**
* According to set flags determine whether NSEC proving
* RRset or RRType non-existence has been found.
* @param f Flags to inspect.
* @return True if required NSEC exists.
*/
#define kr_nsec_rrset_noexist(f) \
((f) & (FLG_NOEXIST_RRTYPE | FLG_NOEXIST_RRSET))
/**
* According to set flags determine whether wildcard non-existence
* has been proven.
* @param f Flags to inspect.
* @return True if wildcard not exists.
*/
#define kr_nsec_wcard_noexist(f) ((f) & FLG_NOEXIST_WILDCARD)
/**
* According to set flags determine whether authenticated denial of existence has been proven.
* @param f Flags to inspect.
* @return True if denial of existence proven.
*/
#define kr_nsec_existence_denied(f) \
((kr_nsec_rrset_noexist(f)) && (kr_nsec_wcard_noexist(f)))
/**
* Name error response check (RFC4035 3.1.3.2; RFC4035 5.4, bullet 2).
* @note Returned flags must be checked in order to prove denial.
* @param flags Flags to be set according to check outcome.
* @param nsec NSEC RR.
* @param name Name to be checked.
* @param pool
* @return 0 or error code.
*/
static int name_error_response_check_rr(int *flags, const knot_rrset_t *nsec,
const knot_dname_t *name)
{
if (kr_fails_assert(flags && nsec && name))
return kr_error(EINVAL);
if (nsec_covers(nsec, name) == 0)
*flags |= FLG_NOEXIST_RRSET;
/* Try to find parent wildcard that is proved by this NSEC. */
uint8_t namebuf[KNOT_DNAME_MAXLEN];
int ret = knot_dname_to_wire(namebuf, name, sizeof(namebuf));
if (ret < 0)
return ret;
knot_dname_t *ptr = namebuf;
while (ptr[0]) {
/* Remove leftmost label and replace it with '\1*'. */
ptr = (uint8_t *) knot_wire_next_label(ptr, NULL);
if (!ptr)
return kr_error(EINVAL);
*(--ptr) = '*';
*(--ptr) = 1;
/* True if this wildcard provably doesn't exist. */
if (nsec_covers(nsec, ptr) == 0) {
*flags |= FLG_NOEXIST_WILDCARD;
break;
}
/* Remove added leftmost asterisk. */
ptr += 2;
}
return kr_ok();
}
int kr_nsec_name_error_response_check(const knot_pkt_t *pkt, knot_section_t section_id,
const knot_dname_t *sname)
{
const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
if (!sec || !sname)
return kr_error(EINVAL);
int flags = 0;
for (unsigned i = 0; i < sec->count; ++i) {
const knot_rrset_t *rrset = knot_pkt_rr(sec, i);
if (rrset->type != KNOT_RRTYPE_NSEC)
continue;
int ret = name_error_response_check_rr(&flags, rrset, sname);
if (ret != 0)
return ret;
}
return kr_nsec_existence_denied(flags) ? kr_ok() : kr_error(ENOENT);
}
/**
* Returns the labels from the covering RRSIG RRs.
* @note The number must be the same in all covering RRSIGs.
* @param nsec NSEC RR.
* @param sec Packet section.
* @param Number of labels or (negative) error code.
*/
static int covering_rrsig_labels(const knot_rrset_t *nsec, const knot_pktsection_t *sec)
{
if (kr_fails_assert(nsec && sec))
return kr_error(EINVAL);
int ret = kr_error(ENOENT);
for (unsigned i = 0; i < sec->count; ++i) {
const knot_rrset_t *rrset = knot_pkt_rr(sec, i);
if ((rrset->type != KNOT_RRTYPE_RRSIG) ||
(!knot_dname_is_equal(rrset->owner, nsec->owner))) {
continue;
}
knot_rdata_t *rdata_j = rrset->rrs.rdata;
for (uint16_t j = 0; j < rrset->rrs.count;
++j, rdata_j = knot_rdataset_next(rdata_j)) {
if (knot_rrsig_type_covered(rdata_j) != KNOT_RRTYPE_NSEC)
continue;
if (ret < 0) {
ret = knot_rrsig_labels(rdata_j);
} else {
if (ret != knot_rrsig_labels(rdata_j)) {
return kr_error(EINVAL);
}
}
}
}
return ret;
}
int kr_nsec_bitmap_nodata_check(const uint8_t *bm, uint16_t bm_size, uint16_t type, const knot_dname_t *owner)
{
const int NO_PROOF = abs(ENOENT);
......@@ -306,114 +174,14 @@ int kr_nsec_bitmap_nodata_check(const uint8_t *bm, uint16_t bm_size, uint16_t ty
return kr_ok();
}
/**
* Attempt to prove NODATA given a matching NSEC.
* @param flags Flags to be set according to check outcome.
* @param nsec NSEC RR.
* @param type Type to be checked.
* @return 0 on success, abs(ENOENT) for no proof, or error code (<0).
* @note It's not a *full* proof, of course (wildcards, etc.)
* @TODO returning result via `flags` is just ugly.
*/
static int no_data_response_check_rrtype(int *flags, const knot_rrset_t *nsec,
uint16_t type)
/// Convenience wrapper for kr_nsec_bitmap_nodata_check()
static int no_data_response_check_rrtype(const knot_rrset_t *nsec, uint16_t type)
{
if (kr_fails_assert(flags && nsec))
if (kr_fails_assert(nsec && nsec->type == KNOT_RRTYPE_NSEC))
return kr_error(EINVAL);
const uint8_t *bm = knot_nsec_bitmap(nsec->rrs.rdata);
uint16_t bm_size = knot_nsec_bitmap_len(nsec->rrs.rdata);
int ret = kr_nsec_bitmap_nodata_check(bm, bm_size, type, nsec->owner);
if (ret == kr_ok())
*flags |= FLG_NOEXIST_RRTYPE;
return ret <= 0 ? ret : kr_ok();
}
/**
* Perform check for RR type wildcard existence denial according to RFC4035 5.4, bullet 1.
* @param flags Flags to be set according to check outcome.
* @param nsec NSEC RR.
* @param sec Packet section to work with.
* @return 0 or error code.
*/
static int no_data_wildcard_existence_check(int *flags, const knot_rrset_t *nsec,
const knot_pktsection_t *sec)
{
if (kr_fails_assert(flags && nsec && sec))
return kr_error(EINVAL);
int rrsig_labels = covering_rrsig_labels(nsec, sec);
if (rrsig_labels < 0)
return rrsig_labels;
int nsec_labels = knot_dname_labels(nsec->owner, NULL);
if (nsec_labels < 0)
return nsec_labels;
if (rrsig_labels == nsec_labels)
*flags |= FLG_NOEXIST_WILDCARD;
return kr_ok();
}
/**
* Perform check for NSEC wildcard existence that covers sname and
* have no stype bit set.
* @param pkt Packet structure to be processed.
* @param sec Packet section to work with.
* @param sname Queried domain name.
* @param stype Queried type.
* @return 0 or error code.
*/
static int wildcard_match_check(const knot_pkt_t *pkt, const knot_pktsection_t *sec,
const knot_dname_t *sname, uint16_t stype)
{
if (!sec || !sname)
return kr_error(EINVAL);
int flags = 0;
for (unsigned i = 0; i < sec->count; ++i) {
const knot_rrset_t *rrset = knot_pkt_rr(sec, i);
if (rrset->type != KNOT_RRTYPE_NSEC)
continue;
if (!knot_dname_is_wildcard(rrset->owner))
continue;
if (!knot_dname_is_equal(rrset->owner, sname)) {
int wcard_labels = knot_dname_labels(rrset->owner, NULL);
int common_labels = knot_dname_matched_labels(rrset->owner, sname);
int rrsig_labels = covering_rrsig_labels(rrset, sec);
if (wcard_labels < 1 ||
common_labels != wcard_labels - 1 ||
common_labels != rrsig_labels) {
continue;
}
}
int ret = no_data_response_check_rrtype(&flags, rrset, stype);
if (ret != 0)
return ret;
}
return (flags & FLG_NOEXIST_RRTYPE) ? kr_ok() : kr_error(ENOENT);
}
int kr_nsec_no_data_response_check(const knot_pkt_t *pkt, knot_section_t section_id,
const knot_dname_t *sname, uint16_t stype)
{
const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
if (!sec || !sname)
return kr_error(EINVAL);
int flags = 0;
for (unsigned i = 0; i < sec->count; ++i) {
const knot_rrset_t *rrset = knot_pkt_rr(sec, i);
if (rrset->type != KNOT_RRTYPE_NSEC)
continue;
if (knot_dname_is_equal(rrset->owner, sname)) {
int ret = no_data_response_check_rrtype(&flags, rrset, stype);
if (ret != 0)
return ret;
}
}
return (flags & FLG_NOEXIST_RRTYPE) ? kr_ok() : kr_error(ENOENT);
return kr_nsec_bitmap_nodata_check(bm, bm_size, type, nsec->owner);
}
int kr_nsec_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_t section_id,
......@@ -434,116 +202,111 @@ int kr_nsec_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_t
return kr_error(ENOENT);
}
int kr_nsec_existence_denial(const knot_pkt_t *pkt, knot_section_t section_id,
const knot_dname_t *sname, uint16_t stype)
int kr_nsec_negative(const ranked_rr_array_t *rrrs, uint32_t qry_uid,
const knot_dname_t *sname, uint16_t stype)
{
const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
if (!sec || !sname)
// We really only consider the (canonically) first NSEC in each RRset.
// Using same owner with differing content probably isn't useful for NSECs anyway.
// Many other parts of code do the same, too.
if (kr_fails_assert(rrrs && sname))
return kr_error(EINVAL);
int flags = 0;
for (unsigned i = 0; i < sec->count; ++i) {
const knot_rrset_t *rrset = knot_pkt_rr(sec, i);
if (rrset->type != KNOT_RRTYPE_NSEC)
continue;
/* NSEC proves that name exists, but has no data (RFC4035 4.9, 1) */
if (knot_dname_is_equal(rrset->owner, sname)) {
no_data_response_check_rrtype(&flags, rrset, stype);
} else {
/* NSEC proves that name doesn't exist (RFC4035, 4.9, 2) */
name_error_response_check_rr(&flags, rrset, sname);
// Terminology: https://datatracker.ietf.org/doc/html/rfc4592#section-3.3.1
int clencl_labels = -1; // the label count of the closest encloser of sname
for (int i = rrrs->len - 1; i >= 0; --i) { // NSECs near the end typically
const knot_rrset_t *nsec = rrrs->at[i]->rr;
bool ok = rrrs->at[i]->qry_uid == qry_uid
&& nsec->type == KNOT_RRTYPE_NSEC
&& kr_rank_test(rrrs->at[i]->rank, KR_RANK_SECURE);
if (!ok) continue;
const int covers = nsec_covers(nsec, sname);
if (covers == abs(EEXIST)
&& no_data_response_check_rrtype(nsec, stype) == 0) {
return PKT_NODATA; // proven NODATA by matching NSEC
}
no_data_wildcard_existence_check(&flags, rrset, sec);
if (covers != 0) continue;
// We have to lower-case 'next' with libknot >= 2.7; see also RFC 6840 5.1.
// LATER(optim.): it's duplicate work with the nsec_covers() call.
knot_dname_t next[KNOT_DNAME_MAXLEN];
int ret = knot_dname_to_wire(next, knot_nsec_next(nsec->rrs.rdata), sizeof(next));
if (kr_fails_assert(ret >= 0))
return kr_error(ret);
knot_dname_to_lower(next);
clencl_labels = MAX(knot_dname_matched_labels(nsec->owner, sname),
knot_dname_matched_labels(sname, next));
break; // reduce indentation again
}
if (kr_nsec_existence_denied(flags)) {
/* denial of existence proved accordingly to 4035 5.4 -
* NSEC proving either rrset non-existence or
* qtype non-existence has been found,
* and no wildcard expansion occurred.
*/
return kr_ok();
} else if (kr_nsec_rrset_noexist(flags)) {
/* NSEC proving either rrset non-existence or
* qtype non-existence has been found,
* but wildcard expansion occurs.
* Try to find matching wildcard and check
* corresponding types.
*/
return wildcard_match_check(pkt, sec, sname, stype);
if (clencl_labels < 0)
return kr_error(ENOENT);
const int sname_labels = knot_dname_labels(sname, NULL);
if (sname_labels == clencl_labels)
return PKT_NODATA; // proven NODATA; sname is an empty non-terminal
// Explicitly construct name for the corresponding source of synthesis.
knot_dname_t ssynth[KNOT_DNAME_MAXLEN + 2];
ssynth[0] = 1;
ssynth[1] = '*';
const knot_dname_t *clencl = sname;
for (int l = sname_labels; l > clencl_labels; --l)
clencl = knot_wire_next_label(clencl, NULL);
(void)!!knot_dname_store(&ssynth[2], clencl);
// Try to (dis)prove the source of synthesis by a covering or matching NSEC.
for (int i = rrrs->len - 1; i >= 0; --i) { // NSECs near the end typically
const knot_rrset_t *nsec = rrrs->at[i]->rr;
bool ok = rrrs->at[i]->qry_uid == qry_uid
&& nsec->type == KNOT_RRTYPE_NSEC
&& kr_rank_test(rrrs->at[i]->rank, KR_RANK_SECURE);
if (!ok) continue;
const int covers = nsec_covers(nsec, ssynth);
if (covers == abs(EEXIST)) {
int ret = no_data_response_check_rrtype(nsec, stype);
if (ret == 0) return PKT_NODATA; // proven NODATA by wildcard NSEC
// TODO: also try expansion? Or at least a different return code?
} else if (covers == 0) {
return PKT_NXDOMAIN | PKT_NODATA;
}
}
return kr_error(ENOENT);
}
int kr_nsec_ref_to_unsigned(const knot_pkt_t *pkt)
int kr_nsec_ref_to_unsigned(const ranked_rr_array_t *rrrs, uint32_t qry_uid,
const knot_dname_t *sname)
{
int nsec_found = 0;
const knot_pktsection_t *sec = knot_pkt_section(pkt, KNOT_AUTHORITY);
if (!sec)
return kr_error(EINVAL);
for (unsigned i = 0; i < sec->count; ++i) {
const knot_rrset_t *ns = knot_pkt_rr(sec, i);
if (ns->type == KNOT_RRTYPE_DS)
return kr_error(EEXIST);
if (ns->type != KNOT_RRTYPE_NS)
continue;
nsec_found = 0;
for (unsigned j = 0; j < sec->count; ++j) {
const knot_rrset_t *nsec = knot_pkt_rr(sec, j);
if (nsec->type == KNOT_RRTYPE_DS)
return kr_error(EEXIST);
if (nsec->type != KNOT_RRTYPE_NSEC)
continue;
/* nsec found
* check if owner name matches the delegation name
*/
if (!knot_dname_is_equal(nsec->owner, ns->owner)) {
/* nsec does not match the delegation */
continue;
}
nsec_found = 1;
const uint8_t *bm = knot_nsec_bitmap(nsec->rrs.rdata);
uint16_t bm_size = knot_nsec_bitmap_len(nsec->rrs.rdata);
if (!bm)
return kr_error(EINVAL);
if (dnssec_nsec_bitmap_contains(bm, bm_size,
KNOT_RRTYPE_NS) &&
!dnssec_nsec_bitmap_contains(bm, bm_size,
KNOT_RRTYPE_DS) &&
!dnssec_nsec_bitmap_contains(bm, bm_size,
KNOT_RRTYPE_SOA)) {
/* rfc4035, 5.2 */
return kr_ok();
}
}
if (nsec_found) {
/* nsec which owner matches
* the delegation name was found,
* but nsec type bitmap contains wrong types
*/
return kr_error(EINVAL);
} else {
/* nsec that matches delegation was not found */
return kr_error(DNSSEC_NOT_FOUND);
}
for (int i = rrrs->len - 1; i >= 0; --i) { // NSECs near the end typically
const knot_rrset_t *nsec = rrrs->at[i]->rr;
bool ok = rrrs->at[i]->qry_uid == qry_uid
&& nsec->type == KNOT_RRTYPE_NSEC
&& kr_rank_test(rrrs->at[i]->rank, KR_RANK_SECURE)
// avoid any possibility of getting tricked in deeper zones
&& knot_dname_in_bailiwick(sname, nsec->owner) >= 0;
if (!ok) continue;
kr_assert(nsec->rrs.rdata);
const uint8_t *bm = knot_nsec_bitmap(nsec->rrs.rdata);
uint16_t bm_size = knot_nsec_bitmap_len(nsec->rrs.rdata);
ok = ok && dnssec_nsec_bitmap_contains(bm, bm_size, KNOT_RRTYPE_NS)
&& !dnssec_nsec_bitmap_contains(bm, bm_size, KNOT_RRTYPE_DS)
&& !dnssec_nsec_bitmap_contains(bm, bm_size, KNOT_RRTYPE_SOA);
if (ok) return kr_ok();
}
return kr_error(EINVAL);
return kr_error(DNSSEC_NOT_FOUND);
}
int kr_nsec_matches_name_and_type(const knot_rrset_t *nsec,
const knot_dname_t *name, uint16_t type)
{
/* It's not secure enough to just check a single bit for (some) other types,
* but we don't (currently) only use this API for NS. See RFC 6840 sec. 4.
*/
if (kr_fails_assert(type == KNOT_RRTYPE_NS && nsec && name))
* but we (currently) only use this API for NS. See RFC 6840 sec. 4. */
if (kr_fails_assert(type == KNOT_RRTYPE_NS && nsec && nsec->rrs.rdata && name))
return kr_error(EINVAL);
if (!knot_dname_is_equal(nsec->owner, name))
return kr_error(ENOENT);
const uint8_t *bm = knot_nsec_bitmap(nsec->rrs.rdata);
uint16_t bm_size = knot_nsec_bitmap_len(nsec->rrs.rdata);
if (!bm)
return kr_error(EINVAL);
if (dnssec_nsec_bitmap_contains(bm, bm_size, type)) {
return kr_ok();
} else {
......
......@@ -6,6 +6,8 @@
#include <libknot/packet/pkt.h>
#include "lib/layer/iterate.h"
/**
* Check bitmap that child names are contained in the same zone.
* @note see RFC6840 4.1.
......@@ -26,28 +28,6 @@ int kr_nsec_children_in_zone_check(const uint8_t *bm, uint16_t bm_size);
*/
int kr_nsec_bitmap_nodata_check(const uint8_t *bm, uint16_t bm_size, uint16_t type, const knot_dname_t *owner);
/**
* Name error response check (RFC4035 3.1.3.2; RFC4035 5.4, bullet 2).
* @note No RRSIGs are validated.
* @param pkt Packet structure to be processed.
* @param section_id Packet section to be processed.
* @param sname Name to be checked.
* @return 0 or error code.
*/
int kr_nsec_name_error_response_check(const knot_pkt_t *pkt, knot_section_t section_id,
const knot_dname_t *sname);
/**
* No data response check (RFC4035 3.1.3.1; RFC4035 5.4, bullet 1).
* @param pkt Packet structure to be processed.
* @param section_id Packet section to be processed.
* @param sname Name to be checked.
* @param stype Type to be checked.
* @return 0 or error code.
*/
int kr_nsec_no_data_response_check(const knot_pkt_t *pkt, knot_section_t section_id,
const knot_dname_t *sname, uint16_t stype);
/**
* Wildcard answer response check (RFC4035 3.1.3.3).
* @param pkt Packet structure to be processed.
......@@ -59,28 +39,24 @@ int kr_nsec_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_t
const knot_dname_t *sname);
/**
* Authenticated denial of existence according to RFC4035 5.4.
* @note No RRSIGs are validated.
* @param pkt Packet structure to be processed.
* @param section_id Packet section to be processed.
* @param sname Queried domain name.
* @param stype Queried type.
* @return 0 or error code.
* Search for a negative proof for sname+stype among validated NSECs.
*
* @param rrrs list of RRs to search; typically kr_request::auth_selected
* @param qry_uid only consider NSECs from this packet, for better efficiency
* @return negative error code, or PKT_NXDOMAIN | PKT_NODATA (both for NXDOMAIN)
*/
int kr_nsec_existence_denial(const knot_pkt_t *pkt, knot_section_t section_id,
const knot_dname_t *sname, uint16_t stype);
int kr_nsec_negative(const ranked_rr_array_t *rrrs, uint32_t qry_uid,
const knot_dname_t *sname, uint16_t stype);
/**
* Referral to unsigned subzone check (RFC4035 5.2).
* @note No RRSIGs are validated.
* @param pkt Packet structure to be processed.
* @return 0 or error code:
* DNSSEC_NOT_FOUND - neither ds nor nsec records
* were not found.
* EEXIST - ds record was found.
* EINVAL - bogus.
*
* @param rrrs list of RRs to search; typically kr_request::auth_selected
* @param qry_uid only consider NSECs from this packet, for better efficiency
* @return 0 or negative error code, in particular DNSSEC_NOT_FOUND
*/
int kr_nsec_ref_to_unsigned(const knot_pkt_t *pkt);
int kr_nsec_ref_to_unsigned(const ranked_rr_array_t *rrrs, uint32_t qry_uid,
const knot_dname_t *sname);
/**
* Checks whether supplied NSEC RR matches the supplied name and type.
......
......@@ -536,10 +536,19 @@ static int update_delegation(struct kr_request *req, struct kr_query *qry, knot_
if (!has_nsec3) {
if (referral) {
/* Check if it is referral to unsigned, rfc4035 5.2 */
ret = kr_nsec_ref_to_unsigned(answer);
ret = kr_nsec_ref_to_unsigned(&req->auth_selected,
qry->uid, proved_name);
} else {
/* No-data answer */
ret = kr_nsec_existence_denial(answer, KNOT_AUTHORITY, proved_name, KNOT_RRTYPE_DS);
ret = kr_nsec_negative(&req->auth_selected, qry->uid,
proved_name, KNOT_RRTYPE_DS);
if (ret >= 0) {
if (ret == PKT_NODATA) {
ret = kr_ok();
} else {
ret = kr_error(ENOENT); // suspicious
}
}
}
} else {
if (referral) {
......@@ -1171,7 +1180,15 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt)
if (!qry->flags.CACHED && pkt_rcode == KNOT_RCODE_NXDOMAIN && !qry->flags.CNAME) {
/* @todo If knot_pkt_qname(pkt) is used instead of qry->sname then the tests crash. */
if (!has_nsec3) {
ret = kr_nsec_name_error_response_check(pkt, KNOT_AUTHORITY, qry->sname);
ret = kr_nsec_negative(&req->auth_selected, qry->uid,
qry->sname, KNOT_RRTYPE_NULL);
if (ret >= 0) {
if (ret & PKT_NXDOMAIN) {
ret = kr_ok();
} else {
ret = kr_error(ENOENT); // probably proved NODATA
}
}
} else {
ret = kr_nsec3_name_error_response_check(pkt, KNOT_AUTHORITY, qry->sname);
}
......@@ -1202,7 +1219,15 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt)
* ? merge the functionality together to share code/resources
*/
if (!has_nsec3) {
ret = kr_nsec_existence_denial(pkt, KNOT_AUTHORITY, knot_pkt_qname(pkt), knot_pkt_qtype(pkt));
ret = kr_nsec_negative(&req->auth_selected, qry->uid,
knot_pkt_qname(pkt), knot_pkt_qtype(pkt));
if (ret >= 0) {
if (ret == PKT_NODATA) {
ret = kr_ok();
} else {
ret = kr_error(ENOENT); // suspicious
}
}
} else {
ret = kr_nsec3_no_data(pkt, KNOT_AUTHORITY, knot_pkt_qname(pkt), knot_pkt_qtype(pkt));
}
......
......@@ -11,7 +11,7 @@ if [ -z "$GITLAB_CI" ]; then
type -P go >/dev/null || exit 77
echo "Building the dnstap test and its dependencies..."
# some packages may be missing on the system right now
go get github.com/{FiloSottile/gvt,cloudflare/dns,dnstap/golang-dnstap}
go get github.com/{FiloSottile/gvt,cloudflare/dns,dnstap/golang-dnstap,golang/protobuf/proto}
else
# In CI we've prebuilt dependencies into the default GOPATH.
# We're in a scratch container, so we just add the dnstap test inside.
......
Subproject commit c4628156e6cf499e8052c321b24793a176ded494
Subproject commit d331b5013e72601a88158d67677c7e0c44e4d1dd