--- /dev/null
+; config options
+; The island of trust is at example.com
+server:
+ trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b"
+ val-override-date: "20070916134226"
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test validator with insecure delegation and DS negative cache
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.sub.example.com. IN A
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.sub.example.com. IN A
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN DNSKEY
+SECTION ANSWER
+example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+example.com. 3600 IN RRSIG DNSKEY DSA 2 3600 20070926134150 20070829134150 2854 example.com. MCwCFBQRtlR4BEv9ohi+PGFjp+AHsJuHAhRCvz0shggvnvI88DFnBDCczHUcVA== ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response for delegation to sub.example.com.
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.sub.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+sub.example.com. IN NS ns.sub.example.com.
+sub.example.com. IN NSEC www.example.com. NS RRSIG NSEC
+sub.example.com. 3600 IN RRSIG NSEC 3 3 3600 20070926134150 20070829134150 2854 example.com. MCwCFDCaiDM6G+glwNW276HWdH+McmjgAhRSwF5OfimNQCqkWgnYotLOwUghKQ== ;{id = 2854}
+SECTION ADDITIONAL
+ns.sub.example.com. IN A 1.2.3.6
+ENTRY_END
+
+; query for missing DS record.
+; get it from the negative cache instead!
+;ENTRY_BEGIN
+;MATCH opcode qtype qname
+;ADJUST copy_id
+;REPLY QR NOERROR
+;SECTION QUESTION
+;sub.example.com. IN DS
+;SECTION ANSWER
+;SECTION AUTHORITY
+;example.com. IN SOA ns.example.com. h.example.com. 2007090504 1800 1800 2419200 7200
+;example.com. 3600 IN RRSIG SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MCwCFC5uwIHSehZtetK2CMNXttSFUB0XAhROFDAgy/FaxR8zFXJzyPdpQG93Sw== ;{id = 2854}
+;sub.example.com. IN NSEC www.example.com. NS RRSIG NSEC
+;sub.example.com. 3600 IN RRSIG NSEC 3 3 3600 20070926134150 20070829134150 2854 example.com. MCwCFDCaiDM6G+glwNW276HWdH+McmjgAhRSwF5OfimNQCqkWgnYotLOwUghKQ== ;{id = 2854}
+;SECTION ADDITIONAL
+;ns.sub.example.com. IN A 1.2.3.6
+;ENTRY_END
+
+
+RANGE_END
+
+; ns.sub.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.6
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+sub.example.com. IN NS
+SECTION ANSWER
+sub.example.com. IN NS ns.sub.example.com.
+SECTION ADDITIONAL
+ns.sub.example.com. IN A 1.2.3.6
+ENTRY_END
+
+; response to query of interest
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.sub.example.com. IN A
+SECTION ANSWER
+www.sub.example.com. IN A 11.11.11.11
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.sub.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.sub.example.com. IN A
+SECTION ANSWER
+www.sub.example.com. 3600 IN A 11.11.11.11
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+SCENARIO_END
#include "config.h"
#include "validator/val_neg.h"
#include "validator/val_nsec.h"
+#include "validator/val_utils.h"
#include "util/data/dname.h"
#include "util/data/msgreply.h"
#include "util/log.h"
/**
* Find the given zone, from the SOA owner name and class
* @param neg: negative cache
- * @param soa: what to look for.
+ * @param nm: what to look for.
+ * @param len: length of nm
+ * @param dclass: class to look for.
* @return zone or NULL if not found.
*/
static struct val_neg_zone* neg_find_zone(struct val_neg_cache* neg,
- struct ub_packed_rrset_key* soa)
+ uint8_t* nm, size_t len, uint16_t dclass)
{
struct val_neg_zone lookfor;
struct val_neg_zone* result;
lookfor.node.key = &lookfor;
- lookfor.name = soa->rk.dname;
- lookfor.len = soa->rk.dname_len;
+ lookfor.name = nm;
+ lookfor.len = len;
lookfor.labs = dname_count_labels(lookfor.name);
- lookfor.dclass = ntohs(soa->rk.rrset_class);
+ lookfor.dclass = dclass;
result = (struct val_neg_zone*)
rbtree_search(&neg->tree, lookfor.node.key);
/**
* Calculate space needed for zone and all its parents
- * @param soa: with name.
+ * @param d: name of zone
+ * @param len: length of name
* @return size.
*/
-static size_t calc_zone_need(struct ub_packed_rrset_key* soa)
+static size_t calc_zone_need(uint8_t* d, size_t len)
{
- uint8_t* d = soa->rk.dname;
- size_t len = soa->rk.dname_len;
size_t res = sizeof(struct val_neg_zone) + len;
while(!dname_is_root(d)) {
log_assert(len > 1); /* not root label */
* @param nm: name for zone (copied)
* @param nm_len: length of name
* @param labs: labels in name.
- * @param dclass: class of zone.
+ * @param dclass: class of zone, host order.
* @return new zone or NULL on failure
*/
static struct val_neg_zone* neg_setup_zone_node(
/**
* Create a new zone.
* @param neg: negative cache
- * @param soa: what to look for.
+ * @param nm: what to look for.
+ * @param nm_len: length of name.
+ * @param dclass: class of zone, host order.
* @return zone or NULL if out of memory.
*/
static struct val_neg_zone* neg_create_zone(struct val_neg_cache* neg,
- struct ub_packed_rrset_key* soa)
+ uint8_t* nm, size_t nm_len, uint16_t dclass)
{
struct val_neg_zone* zone;
struct val_neg_zone* parent;
struct val_neg_zone* p, *np;
- uint8_t* nm = soa->rk.dname;
- size_t nm_len = soa->rk.dname_len;
int labs = dname_count_labels(nm);
- uint16_t dclass = ntohs(soa->rk.rrset_class);
/* find closest enclosing parent zone that (still) exists */
parent = neg_closest_zone_parent(neg, nm, nm_len, labs, dclass);
soa->rk.dname, LDNS_RR_TYPE_SOA, ntohs(soa->rk.rrset_class));
/* ask for enough space to store all of it */
- need = calc_data_need(rep) + calc_zone_need(soa);
+ need = calc_data_need(rep) +
+ calc_zone_need(soa->rk.dname, soa->rk.dname_len);
lock_basic_lock(&neg->lock);
neg_make_space(neg, need);
/* find or create the zone entry */
- zone = neg_find_zone(neg, soa);
+ zone = neg_find_zone(neg, soa->rk.dname, soa->rk.dname_len,
+ ntohs(soa->rk.rrset_class));
if(!zone) {
- if(!(zone = neg_create_zone(neg, soa))) {
+ if(!(zone = neg_create_zone(neg, soa->rk.dname,
+ soa->rk.dname_len, ntohs(soa->rk.rrset_class)))) {
lock_basic_unlock(&neg->lock);
log_err("out of memory adding negative zone");
return;
verbose(VERB_ALGO, "negcache DLV denial proven");
return 1;
}
+
+/** see if the reply has signed NSEC records and return the signer */
+static uint8_t* reply_nsec_signer(struct reply_info* rep, size_t* signer_len,
+ uint16_t* dclass)
+{
+ size_t i;
+ struct packed_rrset_data* d;
+ uint8_t* s;
+ for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){
+ if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC) {
+ d = (struct packed_rrset_data*)rep->rrsets[i]->
+ entry.data;
+ /* return first signer name of first NSEC */
+ if(d->rrsig_count != 0) {
+ val_find_rrset_signer(rep->rrsets[i],
+ &s, signer_len);
+ if(s && *signer_len) {
+ *dclass = ntohs(rep->rrsets[i]->
+ rk.rrset_class);
+ return s;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep,
+ uint8_t* zone_name)
+{
+ size_t i, need;
+ uint8_t* signer;
+ size_t signer_len;
+ uint16_t dclass;
+ struct val_neg_zone* zone;
+ /* no SOA in this message, find RRSIG over NSEC's signer name.
+ * note the NSEC records are maybe not validated yet */
+ signer = reply_nsec_signer(rep, &signer_len, &dclass);
+ if(!signer)
+ return;
+ if(!dname_subdomain_c(signer, zone_name)) {
+ /* the signer is not in the bailiwick, throw it out */
+ return;
+ }
+
+ log_nametypeclass(VERB_ALGO, "negcache insert referral ",
+ signer, LDNS_RR_TYPE_NS, dclass);
+
+ /* ask for enough space to store all of it */
+ need = calc_data_need(rep) + calc_zone_need(signer, signer_len);
+ lock_basic_lock(&neg->lock);
+ neg_make_space(neg, need);
+
+ /* find or create the zone entry */
+ zone = neg_find_zone(neg, signer, signer_len, dclass);
+ if(!zone) {
+ if(!(zone = neg_create_zone(neg, signer, signer_len,
+ dclass))) {
+ lock_basic_unlock(&neg->lock);
+ log_err("out of memory adding negative zone");
+ return;
+ }
+ }
+
+ /* insert the NSECs */
+ for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){
+ if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC)
+ continue;
+ if(!dname_subdomain_c(rep->rrsets[i]->rk.dname,
+ zone->name)) continue;
+ /* insert NSEC into this zone's tree */
+ neg_insert_data(neg, zone, rep->rrsets[i]);
+ }
+ lock_basic_unlock(&neg->lock);
+}
+
+struct dns_msg*
+val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo,
+ struct regional* region, struct rrset_cache* rrset_cache)
+{
+ struct dns_msg* msg;
+ /* only for DS queries */
+ if(qinfo->qtype != LDNS_RR_TYPE_DS)
+ return NULL;
+
+ /* see if info from neg cache is available */
+
+ return msg;
+}
struct config_file;
struct reply_info;
struct rrset_cache;
+struct regional;
+struct query_info;
+struct dns_msg;
/**
* The negative cache. It is shared between the threads, so locked.
*/
void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep);
+/**
+ * Insert NSECs from this referral into the negative cache for reference.
+ * @param neg: negative cache
+ * @param rep: referral reply with NS, NSECs.
+ * @param zone: bailiwick for the referral.
+ * Errors are ignored, means that storage is omitted.
+ */
+void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep,
+ uint8_t* zone);
+
/**
* Perform a DLV style lookup
* During the lookup, we could find out that data has expired. In that
int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len,
uint16_t qclass, struct rrset_cache* rrset_cache, uint32_t now);
+/**
+ * For the given query, try to get a reply out of the negative cache.
+ * The reply still needs to be validated.
+ * @param neg: negative cache.
+ * @param qinfo: query
+ * @param region: where to allocate reply.
+ * @param rrset_cache: rrset cache.
+ * @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);
+
#endif /* VALIDATOR_VAL_NEG_H */