+8 October 2008: Wouter
+ - NSEC negative cache for DS.
+
6 October 2008: Wouter
- jostle-timeout option, so you can config for slow links.
- 0x20 fallback code. Tries 3xnumber of nameserver addresses
#include "iterator/iter_resptype.h"
#include "iterator/iter_scrub.h"
#include "iterator/iter_priv.h"
+#include "validator/val_neg.h"
#include "services/cache/dns.h"
#include "services/cache/infra.h"
#include "util/module.h"
msg = dns_cache_lookup(qstate->env, iq->qchase.qname,
iq->qchase.qname_len, iq->qchase.qtype,
iq->qchase.qclass, qstate->region, qstate->env->scratch);
+ if(!msg && qstate->env->neg_cache) {
+ /* lookup in negative cache; may result in
+ * 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->now);
+ }
if(msg) {
/* handle positive cache response */
enum response_type type = response_type_from_cache(msg,
}
}
-/** create referral message with NS and query */
-static struct dns_msg*
-create_msg(uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
- struct regional* region, struct ub_packed_rrset_key* nskey,
- struct packed_rrset_data* nsdata, uint32_t now)
+struct dns_msg*
+dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype,
+ uint16_t qclass, struct regional* region, size_t capacity)
{
struct dns_msg* msg = (struct dns_msg*)regional_alloc(region,
sizeof(struct dns_msg));
msg->qinfo.qtype = qtype;
msg->qinfo.qclass = qclass;
/* non-packed reply_info, because it needs to grow the array */
- msg->rep = (struct reply_info*)regional_alloc(region,
+ msg->rep = (struct reply_info*)regional_alloc_zero(region,
sizeof(struct reply_info)-sizeof(struct rrset_ref));
if(!msg->rep)
return NULL;
- memset(msg->rep, 0,
- sizeof(struct reply_info)-sizeof(struct rrset_ref));
msg->rep->flags = BIT_QR; /* with QR, no AA */
msg->rep->qdcount = 1;
- /* allocate the array to as much as we could need:
- * NS rrset + DS/NSEC rrset +
- * A rrset for every NS RR
- * AAAA rrset for every NS RR
- */
msg->rep->rrsets = (struct ub_packed_rrset_key**)
regional_alloc(region,
- (2 + nsdata->count*2)*sizeof(struct ub_packed_rrset_key*));
+ capacity*sizeof(struct ub_packed_rrset_key*));
if(!msg->rep->rrsets)
return NULL;
- msg->rep->rrsets[0] = packed_rrset_copy_region(nskey, region, now);
- if(!msg->rep->rrsets[0])
- return NULL;
- msg->rep->ns_numrrsets++;
- msg->rep->rrset_count++;
return msg;
}
+int
+dns_msg_authadd(struct dns_msg* msg, struct regional* region,
+ struct ub_packed_rrset_key* rrset, uint32_t now)
+{
+ if(!(msg->rep->rrsets[msg->rep->rrset_count++] =
+ packed_rrset_copy_region(rrset, region, now)))
+ return 0;
+ msg->rep->ns_numrrsets++;
+ return 1;
+}
+
struct delegpt*
dns_cache_find_delegation(struct module_env* env, uint8_t* qname,
size_t qnamelen, uint16_t qtype, uint16_t qclass,
}
/* create referral message */
if(msg) {
- *msg = create_msg(qname, qnamelen, qtype, qclass, region,
- nskey, nsdata, now);
- if(!*msg) {
+ /* allocate the array to as much as we could need:
+ * NS rrset + DS/NSEC rrset +
+ * A rrset for every NS RR
+ * AAAA rrset for every NS RR
+ */
+ *msg = dns_msg_create(qname, qnamelen, qtype, qclass, region,
+ 2 + nsdata->count*2);
+ if(!*msg || !dns_msg_authadd(*msg, region, nskey, now)) {
lock_rw_unlock(&nskey->entry.lock);
log_err("find_delegation: out of memory");
return NULL;
int cache_fill_missing(struct module_env* env, uint16_t qclass,
struct regional* region, struct delegpt* dp);
+/**
+ * Utility, create new, unpacked data structure for cache response.
+ * QR bit set, no AA. Query set as indicated. Space for number of rrsets.
+ * @param qname: query section name
+ * @param qnamelen: len of qname
+ * @param qtype: query section type
+ * @param qclass: query section class
+ * @param region: where to alloc.
+ * @param capacity: number of rrsets space to create in the array.
+ * @return new dns_msg struct or NULL on mem fail.
+ */
+struct dns_msg* dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype,
+ uint16_t qclass, struct regional* region, size_t capacity);
+
+/**
+ * Add rrset to authority section in unpacked dns_msg message. Must have enough
+ * space left, does not grow the array.
+ * @param msg: msg to put it in.
+ * @param region: region to alloc in
+ * @param rrset: to add in authority section
+ * @param now: now.
+ * @return true if worked, false on fail
+ */
+int dns_msg_authadd(struct dns_msg* msg, struct regional* region,
+ struct ub_packed_rrset_key* rrset, uint32_t now);
+
#endif /* SERVICES_CACHE_DNS_H */
#include "util/net_help.h"
#include "util/config_file.h"
#include "services/cache/rrset.h"
+#include "services/cache/dns.h"
int val_neg_data_compare(const void* a, const void* b)
{
lock_basic_unlock(&neg->lock);
}
+/**
+ * See if rrset exists in rrset cache.
+ * If it does, the bit is checked, and if not expired, it is returned
+ * allocated in region.
+ * @param rrset_cache: rrset cache
+ * @param qname: to lookup rrset name
+ * @param qname_len: length of qname.
+ * @param qtype: type of rrset to lookup, host order
+ * @param qclass: class of rrset to lookup, host order
+ * @param flags: flags for rrset to lookup
+ * @param region: where to alloc result
+ * @param checkbit: if true, a bit in the nsec typemap is checked for absence.
+ * @param checktype: which bit to check
+ * @param now: to check ttl against
+ * @return rrset or NULL
+ */
+static struct ub_packed_rrset_key*
+grab_nsec(struct rrset_cache* rrset_cache, uint8_t* qname, size_t qname_len,
+ uint16_t qtype, uint16_t qclass, uint32_t flags,
+ struct regional* region, int checkbit, uint16_t checktype,
+ uint32_t now)
+{
+ struct ub_packed_rrset_key* r, *k = rrset_cache_lookup(rrset_cache,
+ qname, qname_len, qtype, qclass, flags, now, 0);
+ struct packed_rrset_data* d;
+ if(!k) return NULL;
+ d = (struct packed_rrset_data*)k->entry.data;
+ if(d->ttl < now) {
+ lock_rw_unlock(&k->entry.lock);
+ return NULL;
+ }
+ /* only secure or unchecked records that have signatures. */
+ if( ! ( d->security == sec_status_secure ||
+ (d->security == sec_status_unchecked &&
+ d->rrsig_count > 0) ) ) {
+ lock_rw_unlock(&k->entry.lock);
+ return NULL;
+ }
+ /* check if checktype is absent */
+ if(checkbit && qtype == LDNS_RR_TYPE_NSEC &&
+ nsec_has_type(k, checktype)) {
+ lock_rw_unlock(&k->entry.lock);
+ return NULL;
+ }
+ /* looks OK! copy to region and return it */
+ r = packed_rrset_copy_region(k, region, now);
+ /* if it failed, we return the NULL */
+ lock_rw_unlock(&k->entry.lock);
+ return r;
+}
+
struct dns_msg*
val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo,
- struct regional* region, struct rrset_cache* rrset_cache)
+ struct regional* region, struct rrset_cache* rrset_cache, uint32_t now)
{
struct dns_msg* msg;
+ struct ub_packed_rrset_key* rrset;
+ uint8_t* zname;
+ size_t zname_len;
+ int zname_labs;
+ struct val_neg_zone* zone;
+ struct val_neg_data* data;
/* only for DS queries */
if(qinfo->qtype != LDNS_RR_TYPE_DS)
return NULL;
- /* see if info from neg cache is available */
+ /* see if info from neg cache is available
+ * For NSECs, because there is no optout; a DS next to a delegation
+ * always has exactly an NSEC for it itself; check its DS bit.
+ * flags=0 (not the zone apex).
+ */
+ rrset = grab_nsec(rrset_cache, qinfo->qname, qinfo->qname_len,
+ LDNS_RR_TYPE_NSEC, qinfo->qclass, 0, region, 1,
+ qinfo->qtype, now);
+ if(rrset) {
+ /* return msg with that rrset */
+ if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len,
+ qinfo->qtype, qinfo->qclass, region, 1)))
+ return NULL;
+ if(!dns_msg_authadd(msg, region, rrset, now))
+ return NULL;
+ return msg;
+ }
+
+ /* check NSEC3 neg cache for type DS */
+ /* need to look one zone higher for DS type */
+ zname = qinfo->qname;
+ zname_len = qinfo->qname_len;
+ dname_remove_label(&zname, &zname_len);
+ zname_labs = dname_count_labels(zname);
+
+ /* lookup closest zone */
+ lock_basic_lock(&neg->lock);
+ zone = neg_closest_zone_parent(neg, zname, zname_len, zname_labs,
+ qinfo->qclass);
+ while(zone && !zone->in_use)
+ zone = zone->parent;
+ if(!zone) {
+ lock_basic_unlock(&neg->lock);
+ return NULL;
+ }
+
+ /* lookup closest data record TODO hash NSEC3 */
+ (void)neg_closest_data(zone, qinfo->qname, qinfo->qname_len,
+ zname_labs+1, &data);
+ while(data && !data->in_use)
+ data = data->parent;
+ if(!data) {
+ lock_basic_unlock(&neg->lock);
+ return 0;
+ }
- return msg;
+ /* get RR and check */
+
+ return 0;
}
* @param qinfo: query
* @param region: where to allocate reply.
* @param rrset_cache: rrset cache.
+ * @param now: to check TTLs against.
* @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);
+ struct rrset_cache* rrset_cache, uint32_t now);
#endif /* VALIDATOR_VAL_NEG_H */
return 0;
}
-/**
- * Check if type is present in the NSEC typemap
- * @param nsec: the nsec RRset.
- * If there are multiple RRs, then each must have the same typemap,
- * since the typemap represents the types at this domain node.
- * @param type: type to check for, host order.
- * @return true if present
- */
-static int
+int
nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)nsec->
*/
int nsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type);
+/**
+ * Check if type is present in the NSEC typemap
+ * @param nsec: the nsec RRset.
+ * If there are multiple RRs, then each must have the same typemap,
+ * since the typemap represents the types at this domain node.
+ * @param type: type to check for, host order.
+ * @return true if present
+ */
+int nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type);
+
/**
* Determine if a NSEC proves the NOERROR/NODATA conditions. This will also
* handle the empty non-terminal (ENT) case and partially handle the