mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-01-18 12:42:54 -05:00
- Fix SOA excluded from negative DS responses. Reported by Hauke
Lampe. The negative cache did not include proper SOA records for negative qtype DS responses which makes BIND barf on it, such responses are now only used internally. - Fix negative cache lookup of closestencloser check of DS type bit. git-svn-id: file:///svn/unbound/trunk@1932 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
90da0cc2e8
commit
c68aebb3d7
7 changed files with 159 additions and 7 deletions
|
|
@ -1,6 +1,11 @@
|
|||
9 December 2009: Wouter
|
||||
- Fix Bug#287(reopened): update of ldns tarball with fix for parse
|
||||
errors generated for domain names like '.example.com'.
|
||||
- Fix SOA excluded from negative DS responses. Reported by Hauke
|
||||
Lampe. The negative cache did not include proper SOA records for
|
||||
negative qtype DS responses which makes BIND barf on it, such
|
||||
responses are now only used internally.
|
||||
- Fix negative cache lookup of closestencloser check of DS type bit.
|
||||
|
||||
8 December 2009: Wouter
|
||||
- Fix for lookup of parent-child disagreement domains, where the
|
||||
|
|
|
|||
|
|
@ -872,7 +872,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
|||
* NOERROR/NODATA or NXDOMAIN answers that need validation */
|
||||
msg = val_neg_getmsg(qstate->env->neg_cache, &iq->qchase,
|
||||
qstate->region, qstate->env->rrset_cache,
|
||||
qstate->env->scratch_buffer, *qstate->env->now);
|
||||
qstate->env->scratch_buffer,
|
||||
*qstate->env->now, 1/*add SOA*/);
|
||||
}
|
||||
}
|
||||
if(msg) {
|
||||
|
|
|
|||
|
|
@ -1093,6 +1093,25 @@ void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep,
|
|||
lock_basic_unlock(&neg->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that an NSEC3 rrset does not have a type set.
|
||||
* None of the nsec3s in a hash-collision are allowed to have the type.
|
||||
* (since we do not know which one is the nsec3 looked at, flags, ..., we
|
||||
* ignore the cached item and let it bypass negative caching).
|
||||
* @param k: the nsec3 rrset to check.
|
||||
* @param t: type to check
|
||||
* @return true if no RRs have the type.
|
||||
*/
|
||||
static int nsec3_no_type(struct ub_packed_rrset_key* k, uint16_t t)
|
||||
{
|
||||
int count = (int)((struct packed_rrset_data*)k->entry.data)->count;
|
||||
int i;
|
||||
for(i=0; i<count; i++)
|
||||
if(nsec3_has_type(k, i, t))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* See if rrset exists in rrset cache.
|
||||
* If it does, the bit is checked, and if not expired, it is returned
|
||||
|
|
@ -1132,8 +1151,10 @@ grab_nsec(struct rrset_cache* rrset_cache, uint8_t* qname, size_t qname_len,
|
|||
return NULL;
|
||||
}
|
||||
/* check if checktype is absent */
|
||||
if(checkbit && qtype == LDNS_RR_TYPE_NSEC &&
|
||||
nsec_has_type(k, checktype)) {
|
||||
if(checkbit && (
|
||||
(qtype == LDNS_RR_TYPE_NSEC && nsec_has_type(k, checktype)) ||
|
||||
(qtype == LDNS_RR_TYPE_NSEC3 && !nsec3_no_type(k, checktype))
|
||||
)) {
|
||||
lock_rw_unlock(&k->entry.lock);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -1300,8 +1321,9 @@ neg_nsec3_proof_ds(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len,
|
|||
* ce_rrset equals a closer encloser.
|
||||
* nc_rrset is optout.
|
||||
* No need to check wildcard for type DS */
|
||||
/* capacity=3: ce + nc + soa(if needed) */
|
||||
if(!(msg = dns_msg_create(qname, qname_len,
|
||||
LDNS_RR_TYPE_DS, zone->dclass, region, 2)))
|
||||
LDNS_RR_TYPE_DS, zone->dclass, region, 3)))
|
||||
return NULL;
|
||||
/* now=0 because TTL was reduced in grab_nsec */
|
||||
if(!dns_msg_authadd(msg, region, ce_rrset, 0))
|
||||
|
|
@ -1313,10 +1335,48 @@ neg_nsec3_proof_ds(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add SOA record for external responses.
|
||||
* @param rrset_cache: to look into.
|
||||
* @param now: current time.
|
||||
* @param region: where to perform the allocation
|
||||
* @param msg: current msg with NSEC.
|
||||
* @param zone: val_neg_zone if we have one.
|
||||
* @return false on lookup or alloc failure.
|
||||
*/
|
||||
static int add_soa(struct rrset_cache* rrset_cache, uint32_t now,
|
||||
struct regional* region, struct dns_msg* msg, struct val_neg_zone* zone)
|
||||
{
|
||||
struct ub_packed_rrset_key* soa;
|
||||
uint8_t* nm;
|
||||
size_t nmlen;
|
||||
uint16_t dclass;
|
||||
if(zone) {
|
||||
nm = zone->name;
|
||||
nmlen = zone->len;
|
||||
dclass = zone->dclass;
|
||||
} else {
|
||||
/* Assumes the signer is the zone SOA to add */
|
||||
nm = reply_nsec_signer(msg->rep, &nmlen, &dclass);
|
||||
if(!nm)
|
||||
return 0;
|
||||
}
|
||||
soa = rrset_cache_lookup(rrset_cache, nm, nmlen, LDNS_RR_TYPE_SOA,
|
||||
dclass, 0, now, 0);
|
||||
if(!soa)
|
||||
return 0;
|
||||
if(!dns_msg_authadd(msg, region, soa, now)) {
|
||||
lock_rw_unlock(&soa->entry.lock);
|
||||
return 0;
|
||||
}
|
||||
lock_rw_unlock(&soa->entry.lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct dns_msg*
|
||||
val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo,
|
||||
struct regional* region, struct rrset_cache* rrset_cache,
|
||||
ldns_buffer* buf, uint32_t now)
|
||||
ldns_buffer* buf, uint32_t now, int addsoa)
|
||||
{
|
||||
struct dns_msg* msg;
|
||||
struct ub_packed_rrset_key* rrset;
|
||||
|
|
@ -1340,11 +1400,13 @@ val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo,
|
|||
if(rrset) {
|
||||
/* return msg with that rrset */
|
||||
if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len,
|
||||
qinfo->qtype, qinfo->qclass, region, 1)))
|
||||
qinfo->qtype, qinfo->qclass, region, 2)))
|
||||
return NULL;
|
||||
/* TTL already subtracted in grab_nsec */
|
||||
if(!dns_msg_authadd(msg, region, rrset, 0))
|
||||
return NULL;
|
||||
if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL))
|
||||
return NULL;
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
|
@ -1368,6 +1430,10 @@ val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo,
|
|||
|
||||
msg = neg_nsec3_proof_ds(zone, qinfo->qname, qinfo->qname_len,
|
||||
zname_labs+1, buf, rrset_cache, region, now);
|
||||
if(addsoa && !add_soa(rrset_cache, now, region, msg, zone)) {
|
||||
lock_basic_unlock(&neg->lock);
|
||||
return NULL;
|
||||
}
|
||||
lock_basic_unlock(&neg->lock);
|
||||
return msg;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -240,13 +240,16 @@ int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len,
|
|||
* @param rrset_cache: rrset cache.
|
||||
* @param buf: temporary buffer.
|
||||
* @param now: to check TTLs against.
|
||||
* @param addsoa: if true, produce result for external consumption.
|
||||
* if false, do not add SOA - for unbound-internal consumption.
|
||||
* @return a reply message if something was found.
|
||||
* This reply may still need validation.
|
||||
* NULL if nothing found (or out of memory).
|
||||
*/
|
||||
struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg,
|
||||
struct query_info* qinfo, struct regional* region,
|
||||
struct rrset_cache* rrset_cache, ldns_buffer* buf, uint32_t now);
|
||||
struct rrset_cache* rrset_cache, ldns_buffer* buf, uint32_t now,
|
||||
int addsoa);
|
||||
|
||||
|
||||
/**** functions exposed for unit test ****/
|
||||
|
|
|
|||
|
|
@ -44,7 +44,10 @@
|
|||
#include "validator/val_kentry.h"
|
||||
#include "validator/val_sigcrypt.h"
|
||||
#include "validator/val_anchor.h"
|
||||
#include "validator/val_nsec.h"
|
||||
#include "validator/val_neg.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
#include "util/data/dname.h"
|
||||
|
|
@ -881,3 +884,38 @@ int val_has_signed_nsecs(struct reply_info* rep, char** reason)
|
|||
else *reason = "no signatures over NSEC3s";
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dns_msg*
|
||||
val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t c,
|
||||
struct regional* region)
|
||||
{
|
||||
struct dns_msg* msg;
|
||||
struct query_info qinfo;
|
||||
struct ub_packed_rrset_key *rrset = rrset_cache_lookup(
|
||||
env->rrset_cache, nm, nmlen, LDNS_RR_TYPE_DS, c, 0,
|
||||
*env->now, 0);
|
||||
if(rrset) {
|
||||
/* DS rrset exists. Return it to the validator immediately*/
|
||||
struct ub_packed_rrset_key* copy = packed_rrset_copy_region(
|
||||
rrset, region, *env->now);
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
if(!copy)
|
||||
return NULL;
|
||||
msg = dns_msg_create(nm, nmlen, LDNS_RR_TYPE_DS, c, region, 1);
|
||||
if(!msg)
|
||||
return NULL;
|
||||
msg->rep->rrsets[0] = copy;
|
||||
msg->rep->rrset_count++;
|
||||
msg->rep->an_numrrsets++;
|
||||
return msg;
|
||||
}
|
||||
/* lookup in rrset and negative cache for NSEC/NSEC3 */
|
||||
qinfo.qname = nm;
|
||||
qinfo.qname_len = nmlen;
|
||||
qinfo.qtype = LDNS_RR_TYPE_DS;
|
||||
qinfo.qclass = c;
|
||||
/* do not add SOA to reply message, it is going to be used internal */
|
||||
msg = val_neg_getmsg(env->neg_cache, &qinfo, region, env->rrset_cache,
|
||||
env->scratch_buffer, *env->now, 0);
|
||||
return msg;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -321,4 +321,21 @@ int val_has_signed_nsecs(struct reply_info* rep, char** reason);
|
|||
*/
|
||||
int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset);
|
||||
|
||||
/**
|
||||
* Find DS denial message in cache. Saves new qstate allocation and allows
|
||||
* the validator to use partial content which is not enough to construct a
|
||||
* message for network (or user) consumption. Without SOA for example,
|
||||
* which is a common occurence in the unbound code since the referrals contain
|
||||
* NSEC/NSEC3 rrs without the SOA element, thus do not allow synthesis of a
|
||||
* full negative reply, but do allow synthesis of sufficient proof.
|
||||
* @param env: query env with caches and time.
|
||||
* @param nm: name of DS record sought.
|
||||
* @param nmlen: length of name.
|
||||
* @param c: class of DS RR.
|
||||
* @param region: where to allocate result.
|
||||
* @return a dns_msg on success. NULL on failure.
|
||||
*/
|
||||
struct dns_msg* val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen,
|
||||
uint16_t c, struct regional* region);
|
||||
|
||||
#endif /* VALIDATOR_VAL_UTILS_H */
|
||||
|
|
|
|||
|
|
@ -58,6 +58,11 @@
|
|||
#include "util/config_file.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
|
||||
/* forward decl for cache response and normal super inform calls of a DS */
|
||||
static void process_ds_response(struct module_qstate* qstate,
|
||||
struct val_qstate* vq, int id, int rcode, struct dns_msg* msg,
|
||||
struct query_info* qinfo, struct sock_list* origin);
|
||||
|
||||
/** fill up nsec3 key iterations config entry */
|
||||
static int
|
||||
fill_nsec3_iter(struct val_env* ve, char* s, int c)
|
||||
|
|
@ -1457,6 +1462,23 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
|
|||
|
||||
if(!vq->ds_rrset || query_dname_compare(vq->ds_rrset->rk.dname,
|
||||
target_key_name) != 0) {
|
||||
/* check if there is a cache entry : pick up an NSEC if
|
||||
* there is no DS, check if that NSEC has DS-bit unset, and
|
||||
* thus can disprove the secure delagation we seek.
|
||||
* We can then use that NSEC even in the absence of a SOA
|
||||
* record that would be required by the iterator to supply
|
||||
* a completely protocol-correct response.
|
||||
* Uses negative cache for NSEC3 lookup of DS responses. */
|
||||
/* only if cache not blacklisted, of course */
|
||||
struct dns_msg* msg;
|
||||
if(!qstate->blacklist && !vq->chain_blacklist &&
|
||||
(msg=val_find_DS(qstate->env, target_key_name,
|
||||
target_key_len, vq->qchase.qclass, qstate->region)) ) {
|
||||
verbose(VERB_ALGO, "Process cached DS response");
|
||||
process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR,
|
||||
msg, &msg->qinfo, NULL);
|
||||
return 1; /* continue processing ds-response results */
|
||||
}
|
||||
if(!generate_request(qstate, id, target_key_name,
|
||||
target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass,
|
||||
BIT_CD)) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue