diff --git a/NEWS b/NEWS
index c375ed0b0e01ded6b4b4eb45001bcb130a646aa9..b44f6c906e73f877291619e06c1e51f32ce04d2d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,18 @@
+Knot Resolver 1.5.2 (2017-01-22)
+================================
+
+Security
+--------
+- fix CVE-2018-1000002: insufficient DNSSEC validation, allowing
+  attackers to deny existence of some data by forging packets.
+  Some combinations pointed out in RFC 6840 sections 4.1 and 4.3
+  were not taken into account.
+
+Bugfixes
+--------
+- memcached: fix fallout from module rename in 1.5.1
+
+
 Knot Resolver 1.5.1 (2017-12-12)
 ================================
 
diff --git a/config.mk b/config.mk
index fcb62a37b8dbd2ad80e0116a0578efd5c458b1ae..0023b4b5cbc1a3904d57f78de08a0a84fed26fca 100644
--- a/config.mk
+++ b/config.mk
@@ -1,7 +1,7 @@
 # Project
 MAJOR := 1
 MINOR := 5
-PATCH := 1
+PATCH := 2
 EXTRA :=
 ABIVER := 4
 BUILDMODE := dynamic
diff --git a/lib/dnssec/nsec.c b/lib/dnssec/nsec.c
index 3335b3e8f0e8493163f427b64de0422f52beacae..18aaae4e06a736d23cdbc12e78afd29b2eb45f47 100644
--- a/lib/dnssec/nsec.c
+++ b/lib/dnssec/nsec.c
@@ -30,6 +30,7 @@
 bool kr_nsec_bitmap_contains_type(const uint8_t *bm, uint16_t bm_size, uint16_t type)
 {
 	if (!bm || bm_size == 0) {
+		assert(bm);
 		return false;
 	}
 
@@ -59,29 +60,52 @@ bool kr_nsec_bitmap_contains_type(const uint8_t *bm, uint16_t bm_size, uint16_t
 	return false;
 }
 
+int kr_nsec_children_in_zone_check(const uint8_t *bm, uint16_t bm_size)
+{
+	if (!bm) {
+		return kr_error(EINVAL);
+	}
+	const bool parent_side =
+		kr_nsec_bitmap_contains_type(bm, bm_size, KNOT_RRTYPE_DNAME)
+		|| (kr_nsec_bitmap_contains_type(bm, bm_size, KNOT_RRTYPE_NS)
+		    && !kr_nsec_bitmap_contains_type(bm, bm_size, KNOT_RRTYPE_SOA)
+		);
+	return parent_side ? abs(ENOENT) : kr_ok();
+	/* LATER: after refactoring, probably also check if signer name equals owner,
+	 * but even without that it's not possible to attack *correctly* signed zones.
+	 */
+}
+
 /**
  * Check whether the NSEC RR proves that there is no closer match for <SNAME, SCLASS>.
  * @param nsec  NSEC RRSet.
  * @param sname Searched name.
- * @return      0 or error code.
+ * @return      0 if proves, >0 if not (abs(ENOENT)), or error code (<0).
  */
-static int nsec_nonamematch(const knot_rrset_t *nsec, const knot_dname_t *sname)
+static int nsec_covers(const knot_rrset_t *nsec, const knot_dname_t *sname)
 {
 	assert(nsec && sname);
 	const knot_dname_t *next = knot_nsec_next(&nsec->rrs);
+	if (knot_dname_cmp(sname, nsec->owner) <= 0) {
+		return abs(ENOENT); /* 'sname' before 'owner', so can't be covered */
+	}
 	/* If NSEC 'owner' >= 'next', it means that there is nothing after 'owner' */
-	const bool is_last_nsec = (knot_dname_cmp(nsec->owner, next) >= 0);
-	if (is_last_nsec) { /* SNAME is after owner => provably doesn't exist */
-		if (knot_dname_cmp(nsec->owner, sname) < 0) {
-			return kr_ok();
-		}
-	} else {
-		/* Prove that SNAME is between 'owner' and 'next' */
-		if ((knot_dname_cmp(nsec->owner, sname) < 0) && (knot_dname_cmp(sname, next) < 0)) {
-			return kr_ok();
-		}
+	const bool is_last_nsec = knot_dname_cmp(nsec->owner, next) >= 0;
+	const bool in_range = is_last_nsec || knot_dname_cmp(sname, next) < 0;
+	if (!in_range) {
+		return abs(ENOENT);
 	}
-	return kr_error(EINVAL);
+	/* Before returning kr_ok(), we have to check a special case:
+	 * sname might be under delegation from owner and thus
+	 * not in the zone of this NSEC at all.
+	 */
+	if (!knot_dname_is_sub(sname, nsec->owner)) {
+		return kr_ok();
+	}
+	uint8_t *bm = NULL;
+	uint16_t bm_size = 0;
+	knot_nsec_bitmap(&nsec->rrs, &bm, &bm_size);
+	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. */
@@ -128,7 +152,7 @@ static int name_error_response_check_rr(int *flags, const knot_rrset_t *nsec,
 {
 	assert(flags && nsec && name);
 
-	if (nsec_nonamematch(nsec, name) == 0) {
+	if (nsec_covers(nsec, name) == 0) {
 		*flags |= FLG_NOEXIST_RRSET;
 	}
 
@@ -147,7 +171,7 @@ static int name_error_response_check_rr(int *flags, const knot_rrset_t *nsec,
 		*(--ptr) = '*';
 		*(--ptr) = 1;
 		/* True if this wildcard provably doesn't exist. */
-		if (nsec_nonamematch(nsec, ptr) == 0) {
+		if (nsec_covers(nsec, ptr) == 0) {
 			*flags |= FLG_NOEXIST_WILDCARD;
 			break;
 		}
@@ -219,40 +243,74 @@ static int coverign_rrsig_labels(const knot_rrset_t *nsec, const knot_pktsection
 	return ret;
 }
 
-/**
- * Perform check of RR type existence denial according to RFC4035 5.4, bullet 1.
- * @param flags Flags to be set according to check outcome.
- * @param nsec  NSEC RR.
- * @param type  Type to be checked.
- * @return      0 or error code.
- */
-static int no_data_response_check_rrtype(int *flags, const knot_rrset_t *nsec,
-                                         uint16_t type)
-{
-	assert(flags && nsec);
 
-	uint8_t *bm = NULL;
-	uint16_t bm_size;
-	knot_nsec_bitmap(&nsec->rrs, &bm, &bm_size);
+int kr_nsec_bitmap_nodata_check(const uint8_t *bm, uint16_t bm_size, uint16_t type)
+{
+	const int NO_PROOF = abs(ENOENT);
 	if (!bm) {
 		return kr_error(EINVAL);
 	}
+	if (kr_nsec_bitmap_contains_type(bm, bm_size, type)) {
+		return NO_PROOF;
+	}
 
-	if (!kr_nsec_bitmap_contains_type(bm, bm_size, type)) {
-		/* The type is not listed in the NSEC bitmap. */
+	if (type != KNOT_RRTYPE_CNAME
+	    && kr_nsec_bitmap_contains_type(bm, bm_size, KNOT_RRTYPE_CNAME)) {
+		return NO_PROOF;
+	}
+	/* Special behavior around zone cuts. */
+	switch (type) {
+	case KNOT_RRTYPE_DS:
 		/* Security feature: in case of DS also check for SOA
 		 * non-existence to be more certain that we don't hold
 		 * a child-side NSEC by some mistake (e.g. when forwarding).
 		 * See RFC4035 5.2, next-to-last paragraph. */
-		if (type != KNOT_RRTYPE_DS
-		    || !kr_nsec_bitmap_contains_type(bm, bm_size, KNOT_RRTYPE_SOA)) {
-			*flags |= FLG_NOEXIST_RRTYPE;
+		if (kr_nsec_bitmap_contains_type(bm, bm_size, KNOT_RRTYPE_SOA)) {
+			return NO_PROOF;
+		}
+		break;
+	case KNOT_RRTYPE_CNAME:
+		/* Exception from the `default` rule.  It's perhaps disputable,
+		 * but existence of CNAME at zone apex is not allowed, so we
+		 * consider a parent-side record to be enough to prove non-existence. */
+		break;
+	default:
+		/* Parent-side delegation record isn't authoritative for non-DS;
+		 * see RFC6840 4.1. */
+		if (kr_nsec_bitmap_contains_type(bm, bm_size, KNOT_RRTYPE_NS)
+		    && !kr_nsec_bitmap_contains_type(bm, bm_size, KNOT_RRTYPE_SOA)) {
+			return NO_PROOF;
 		}
+		/* LATER(opt): perhaps short-circuit test if we repeat it here. */
 	}
 
 	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)
+{
+	assert(flags && nsec);
+
+	uint8_t *bm = NULL;
+	uint16_t bm_size = 0;
+	knot_nsec_bitmap(&nsec->rrs, &bm, &bm_size);
+	int ret = kr_nsec_bitmap_nodata_check(bm, bm_size, type);
+	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.
@@ -360,7 +418,7 @@ int kr_nsec_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_t
 		if (rrset->type != KNOT_RRTYPE_NSEC) {
 			continue;
 		}
-		if (nsec_nonamematch(rrset, sname) == 0) {
+		if (nsec_covers(rrset, sname) == 0) {
 			return kr_ok();
 		}
 	}
@@ -476,11 +534,15 @@ int kr_nsec_ref_to_unsigned(const knot_pkt_t *pkt)
 int kr_nsec_matches_name_and_type(const knot_rrset_t *nsec,
 				   const knot_dname_t *name, uint16_t type)
 {
-	if (!nsec || !name) {
-		return (EINVAL);
+	/* 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 (type != KNOT_RRTYPE_NS || !nsec || !name) {
+		assert(!EINVAL);
+		return kr_error(EINVAL);
 	}
 	if (!knot_dname_is_equal(nsec->owner, name)) {
-		return (ENOENT);
+		return kr_error(ENOENT);
 	}
 	uint8_t *bm = NULL;
 	uint16_t bm_size = 0;
@@ -488,8 +550,9 @@ int kr_nsec_matches_name_and_type(const knot_rrset_t *nsec,
 	if (!bm) {
 		return kr_error(EINVAL);
 	}
-	if (!kr_nsec_bitmap_contains_type(bm, bm_size, type)) {
-		return (ENOENT);
+	if (kr_nsec_bitmap_contains_type(bm, bm_size, type)) {
+		return kr_ok();
+	} else {
+		return kr_error(ENOENT);
 	}
-	return kr_ok();
 }
diff --git a/lib/dnssec/nsec.h b/lib/dnssec/nsec.h
index 0c4f23d0fc3ebcf0f7642e6067a11a0783f7c362..7d78a8cfb918c752e5e7ce9e069b461f4bfcc64c 100644
--- a/lib/dnssec/nsec.h
+++ b/lib/dnssec/nsec.h
@@ -20,13 +20,32 @@
 
 /**
  * Check whether bitmap contains given type.
- * @param bm      Bitmap.
+ * @param bm      Bitmap from NSEC or NSEC3.
  * @param bm_size Bitmap size.
  * @param type    RR type to search for.
  * @return        True if bitmap contains type.
  */
 bool kr_nsec_bitmap_contains_type(const uint8_t *bm, uint16_t bm_size, uint16_t type);
 
+/**
+ * Check bitmap that child names are contained in the same zone.
+ * @note see RFC6840 4.1.
+ * @param bm      Bitmap from NSEC or NSEC3.
+ * @param bm_size Bitmap size.
+ * @return 0 if they are, >0 if not (abs(ENOENT)), <0 on error.
+ */
+int kr_nsec_children_in_zone_check(const uint8_t *bm, uint16_t bm_size);
+
+/**
+ * Check an NSEC or NSEC3 bitmap for NODATA for a type.
+ * @param bm      Bitmap.
+ * @param bm_size Bitmap size.
+ * @param type    RR type to check.
+ * @note This includes special checks for zone cuts, e.g. from RFC 6840 sec. 4.
+ * @return 0, abs(ENOENT) (no proof), kr_error(EINVAL)
+ */
+int kr_nsec_bitmap_nodata_check(const uint8_t *bm, uint16_t bm_size, uint16_t type);
+
 /**
  * Name error response check (RFC4035 3.1.3.2; RFC4035 5.4, bullet 2).
  * @note No RRSIGs are validated.
@@ -87,7 +106,7 @@ int kr_nsec_ref_to_unsigned(const knot_pkt_t *pkt);
  * Checks whether supplied NSEC RR matches the supplied name and type.
  * @param nsec  NSEC RR.
  * @param name  Name to be checked.
- * @param type  Type to be checked.
+ * @param type  Type to be checked.  Only use with NS!  TODO (+copy&paste NSEC3)
  * @return      0 or error code.
  */
 int kr_nsec_matches_name_and_type(const knot_rrset_t *nsec,
diff --git a/lib/dnssec/nsec3.c b/lib/dnssec/nsec3.c
index 1ebf3850ebe68baec0e170e4d23b8b2a8d3645d1..fd683cb6171ee898e246fb4c100ce61d4bbd5ddc 100644
--- a/lib/dnssec/nsec3.c
+++ b/lib/dnssec/nsec3.c
@@ -299,11 +299,11 @@ static bool has_optout(const knot_rrset_t *nsec3)
  * @param flags Flags to be set according to check outcome.
  * @param nsec3 NSEC3 RR.
  * @param name  Name to be checked.
- * @return      0 or error code.
+ * @return      0 if matching, >0 if not (abs(ENOENT)), or error code (<0).
  */
-static int matches_name(int *flags, const knot_rrset_t *nsec3, const knot_dname_t *name)
+static int matches_name(const knot_rrset_t *nsec3, const knot_dname_t *name)
 {
-	assert(flags && nsec3 && name);
+	assert(nsec3 && name);
 
 	dnssec_binary_t owner_hash = {0, };
 	uint8_t hash_data[MAX_HASH_BYTES] = {0, };
@@ -328,8 +328,9 @@ static int matches_name(int *flags, const knot_rrset_t *nsec3, const knot_dname_
 
 	if ((owner_hash.size == name_hash.size) &&
 	    (memcmp(owner_hash.data, name_hash.data, owner_hash.size) == 0)) {
-		*flags |= FLG_NAME_MATCHED;
 		ret = kr_ok();
+	} else {
+		ret = abs(ENOENT);
 	}
 
 fail:
@@ -390,6 +391,18 @@ static int closest_encloser_proof(const knot_pkt_t *pkt,
 		if (rrset->type != KNOT_RRTYPE_NSEC3) {
 			continue;
 		}
+		/* Also skip the NSEC3-to-match an ancestor of sname if it's
+		 * a parent-side delegation, as that would mean the owner
+		 * does not really exist (authoritatively in this zone,
+		 * even in case of opt-out).
+		 */
+		uint8_t *bm = NULL;
+		uint16_t bm_size;
+		knot_nsec3_bitmap(&rrset->rrs, 0, &bm, &bm_size);
+		if (kr_nsec_children_in_zone_check(bm, bm_size) != 0) {
+			continue; /* no fatal errors from bad RRs */
+		}
+		/* Match the NSEC3 to sname or one of its ancestors. */
 		unsigned skipped = 0;
 		flags = 0;
 		int ret = closest_encloser_match(&flags, rrset, sname, &skipped);
@@ -400,6 +413,7 @@ static int closest_encloser_proof(const knot_pkt_t *pkt,
 			continue;
 		}
 		matching = rrset;
+		/* Construct the next closer name and try to cover it. */
 		--skipped;
 		next_closer = sname;
 		for (unsigned j = 0; j < skipped; ++j) {
@@ -512,81 +526,36 @@ int kr_nsec3_name_error_response_check(const knot_pkt_t *pkt, knot_section_t sec
 }
 
 /**
- * Checks whether supplied NSEC3 RR matches the supplied name and type.
- * @param flags Flags to be set according to check outcome.
- * @param nsec3 NSEC3 RR.
- * @param name  Name to be checked.
- * @param type  Type to be checked.
- * @return      0 or error code.
- */
-static int matches_name_and_type(int *flags, const knot_rrset_t *nsec3,
-                                const knot_dname_t *name, uint16_t type)
-{
-	assert(flags && nsec3 && name);
-
-	int ret = matches_name(flags, nsec3, name);
-	if (ret != 0) {
-		return ret;
-	}
-
-	if (!(*flags & FLG_NAME_MATCHED)) {
-		return kr_ok();
-	}
-
-	uint8_t *bm = NULL;
-	uint16_t bm_size;
-	knot_nsec3_bitmap(&nsec3->rrs, 0, &bm, &bm_size);
-	if (!bm) {
-		return kr_error(EINVAL);
-	}
-
-	if (!kr_nsec_bitmap_contains_type(bm, bm_size, type)) {
-		*flags |= FLG_TYPE_BIT_MISSING;
-		if (type == KNOT_RRTYPE_CNAME) {
-			*flags |= FLG_CNAME_BIT_MISSING;
-		}
-	}
-
-	if ((type != KNOT_RRTYPE_CNAME) &&
-	    !kr_nsec_bitmap_contains_type(bm, bm_size, KNOT_RRTYPE_CNAME)) {
-		*flags |= FLG_CNAME_BIT_MISSING;
-	}
-
-	return kr_ok();
-}
-
-/**
- * No data response check, no DS (RFC5155 7.2.3).
+ * Search the packet section for a matching NSEC3 with nodata-proving bitmap.
  * @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.
+ * @note             This does NOT check the opt-out case if type is DS;
+ *                   see RFC 5155 8.6.
  */
-static int no_data_response_no_ds(const knot_pkt_t *pkt, knot_section_t section_id,
-                                  const knot_dname_t *sname, uint16_t stype)
+static int nodata_find(const knot_pkt_t *pkt, knot_section_t section_id,
+			const knot_dname_t *name, const uint16_t type)
 {
 	const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
-	if (!sec || !sname) {
+	if (!sec || !name) {
 		return kr_error(EINVAL);
 	}
 
-	int flags;
 	for (unsigned i = 0; i < sec->count; ++i) {
-		const knot_rrset_t *rrset = knot_pkt_rr(sec, i);
-		if (rrset->type != KNOT_RRTYPE_NSEC3) {
+		const knot_rrset_t *nsec3 = knot_pkt_rr(sec, i);
+		/* Records causing any errors are simply skipped. */
+		if (nsec3->type != KNOT_RRTYPE_NSEC3
+		    || matches_name(nsec3, name) != kr_ok()) {
 			continue;
+			/* LATER(optim.): we repeatedly recompute the hash of `name` */
 		}
-		flags = 0;
 
-		int ret = matches_name_and_type(&flags, rrset, sname, stype);
-		if (ret != 0) {
-			return ret;
-		}
-
-		if ((flags & FLG_NAME_MATCHED) &&
-		    (flags & FLG_TYPE_BIT_MISSING) &&
-		    (flags & FLG_CNAME_BIT_MISSING)) {
+		uint8_t *bm = NULL;
+		uint16_t bm_size;
+		knot_nsec3_bitmap(&nsec3->rrs, 0, &bm, &bm_size);
+		if (kr_nsec_bitmap_nodata_check(bm, bm_size, type) == kr_ok()) {
 			return kr_ok();
 		}
 	}
@@ -610,38 +579,13 @@ static int matches_closest_encloser_wildcard(const knot_pkt_t *pkt, knot_section
 		return kr_error(EINVAL);
 	}
 
-	uint8_t wildcard[KNOT_DNAME_MAXLEN];
+	uint8_t wildcard[KNOT_DNAME_MAXLEN]; /**< the source of synthesis */
 	int ret = prepend_asterisk(wildcard, sizeof(wildcard), encloser);
 	if (ret < 0) {
 		return ret;
 	}
 	assert(ret >= 3);
-
-	int flags;
-	for (unsigned i = 0; i < sec->count; ++i) {
-		const knot_rrset_t *rrset = knot_pkt_rr(sec, i);
-		if (rrset->type != KNOT_RRTYPE_NSEC3) {
-			continue;
-		}
-		flags = 0;
-
-		int ret = matches_name_and_type(&flags, rrset, wildcard, stype);
-		if (ret != 0) {
-			return ret;
-		}
-
-		/* TODO -- The loop resembles no_data_response_no_ds() exept
-		 * the following condition.
-		 */
-		if ((flags & FLG_NAME_MATCHED) &&
-		    (flags & FLG_TYPE_BIT_MISSING) &&
-		    (flags & FLG_CNAME_BIT_MISSING)) {
-			/* rfc5155 8.7 */
-			return kr_ok();
-		}
-	}
-
-	return kr_error(ENOENT);
+	return nodata_find(pkt, section_id, wildcard, stype);
 }
 
 int kr_nsec3_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_t section_id,
@@ -682,7 +626,7 @@ int kr_nsec3_no_data(const knot_pkt_t *pkt, knot_section_t section_id,
                      const knot_dname_t *sname, uint16_t stype)
 {
 	/* DS record may be also matched by an existing NSEC3 RR. */
-	int ret = no_data_response_no_ds(pkt, section_id, sname, stype);
+	int ret = nodata_find(pkt, section_id, sname, stype);
 	if (ret == 0) {
 		/* Satisfies RFC5155 8.5 and 8.6, both first paragraph. */
 		return ret;
@@ -737,7 +681,6 @@ int kr_nsec3_ref_to_unsigned(const knot_pkt_t *pkt)
 	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) {
@@ -749,7 +692,6 @@ int kr_nsec3_ref_to_unsigned(const knot_pkt_t *pkt)
 
 		int flags = 0;
 		bool nsec3_found = false;
-
 		for (unsigned j = 0; j < sec->count; ++j) {
 			const knot_rrset_t *nsec3 = knot_pkt_rr(sec, j);
 			if (nsec3->type == KNOT_RRTYPE_DS) {
@@ -759,17 +701,9 @@ int kr_nsec3_ref_to_unsigned(const knot_pkt_t *pkt)
 				continue;
 			}
 			nsec3_found = true;
-			/* nsec3 found, check if owner name matches
-			 * the delegation name
-			 */
-			int ret = matches_name(&flags, nsec3, ns->owner);
-			if (ret != 0) {
-				return kr_error(EINVAL);
-			}
-			if (!(flags & FLG_NAME_MATCHED)) {
-				/* nsec3 owner name does not match
-				 * the delegation name
-				 */
+			/* nsec3 found, check if owner name matches the delegation name.
+			 * Just skip in case of *any* errors. */
+			if (matches_name(nsec3, ns->owner) != kr_ok()) {
 				continue;
 			}
 
@@ -805,8 +739,8 @@ int kr_nsec3_ref_to_unsigned(const knot_pkt_t *pkt)
 		 */
 		const knot_dname_t *encloser_name = NULL;
 		const knot_rrset_t *covering_next_nsec3 = NULL;
-		int ret = closest_encloser_proof(pkt, KNOT_AUTHORITY, ns->owner, &encloser_name,
-                                     NULL, &covering_next_nsec3);
+		int ret = closest_encloser_proof(pkt, KNOT_AUTHORITY, ns->owner,
+				&encloser_name, NULL, &covering_next_nsec3);
 		if (ret != 0) {
 			return kr_error(EINVAL);
 		}
@@ -823,11 +757,26 @@ int kr_nsec3_ref_to_unsigned(const knot_pkt_t *pkt)
 int kr_nsec3_matches_name_and_type(const knot_rrset_t *nsec3,
 				   const knot_dname_t *name, uint16_t type)
 {
-	int flags = 0;
-	int ret = matches_name_and_type(&flags, nsec3, name, type);
-	if (ret != kr_ok()) {
-		return ret;
+	/* 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 (type != KNOT_RRTYPE_NS) {
+		assert(!EINVAL);
+		return kr_error(EINVAL);
+	}
+	int ret = matches_name(nsec3, name);
+	if (ret) {
+		return kr_error(ret);
+	}
+	uint8_t *bm = NULL;
+	uint16_t bm_size = 0;
+	knot_nsec3_bitmap(&nsec3->rrs, 0, &bm, &bm_size);
+	if (!bm) {
+		return kr_error(EINVAL);
+	}
+	if (kr_nsec_bitmap_contains_type(bm, bm_size, type)) {
+		return kr_ok();
+	} else {
+		return kr_error(ENOENT);
 	}
-	return ((flags & (FLG_NAME_MATCHED | FLG_TYPE_BIT_MISSING)) != FLG_NAME_MATCHED) ?
-	       kr_error(ENOENT) : kr_ok();
 }
diff --git a/lib/dnssec/nsec3.h b/lib/dnssec/nsec3.h
index b07af7e533e8f8bc5023293e3fdc6eead17a5c8c..33a396a468f0f253eb3a8c3f6ea8427459d400bc 100644
--- a/lib/dnssec/nsec3.h
+++ b/lib/dnssec/nsec3.h
@@ -72,10 +72,10 @@ int kr_nsec3_no_data(const knot_pkt_t *pkt, knot_section_t section_id,
 int kr_nsec3_ref_to_unsigned(const knot_pkt_t *pkt);
 
 /**
- * Checks whether supplied NSEC3 RR matches the supplied name and type.
+ * Checks whether supplied NSEC3 RR matches the supplied name and NS type.
  * @param nsec3 NSEC3 RR.
  * @param name  Name to be checked.
- * @param type  Type to be checked.
+ * @param type  Type to be checked.  Only use with NS!  TODO
  * @return      0 or error code.
  */
 int kr_nsec3_matches_name_and_type(const knot_rrset_t *nsec3,