2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 10:10:06 +00:00

4693. [func] Synthesis of responses from DNSSEC-verified records.

Stage 1 covers NXDOMAIN synthesis from NSEC records.
                        This is controlled by synth-from-dnssec and is enabled
                        by default. [RT #40138]
This commit is contained in:
Mark Andrews 2017-08-31 07:57:50 +10:00
parent c26370fc69
commit 0aed466565
32 changed files with 927 additions and 98 deletions

View File

@ -1,3 +1,8 @@
4693. [func] Synthesis of responses from DNSSEC-verified records.
Stage 1 covers NXDOMAIN synthesis from NSEC records.
This is controlled by synth-from-dnssec and is enabled
by default. [RT #40138]
4692. [bug] Fix build failures with libressl introduced in 4676. 4692. [bug] Fix build failures with libressl introduced in 4676.
[RT #45879] [RT #45879]

View File

@ -128,55 +128,40 @@ options {\n\
dnssec-lookaside . trust-anchor dlv.isc.org;\n\ dnssec-lookaside . trust-anchor dlv.isc.org;\n\
\n\ \n\
/* view */\n\ /* view */\n\
allow-new-zones no;\n\
allow-notify {none;};\n\ allow-notify {none;};\n\
allow-update-forwarding {none;};\n\
allow-query-cache { localnets; localhost; };\n\ allow-query-cache { localnets; localhost; };\n\
allow-query-cache-on { any; };\n\ allow-query-cache-on { any; };\n\
allow-recursion { localnets; localhost; };\n\ allow-recursion { localnets; localhost; };\n\
allow-recursion-on { any; };\n\ allow-recursion-on { any; };\n\
allow-update-forwarding {none;};\n\
# allow-v6-synthesis <obsolete>;\n\ # allow-v6-synthesis <obsolete>;\n\
# sortlist <none>\n\
# topology <none>\n\
auth-nxdomain false;\n\ auth-nxdomain false;\n\
glue-cache yes;\n\
minimal-any false;\n\
minimal-responses true;\n\
recursion true;\n\
provide-ixfr true;\n\
request-ixfr true;\n\
request-expire true;\n\
# fetch-glue <obsolete>;\n\
# rfc2308-type1 <obsolete>;\n\
query-source address *;\n\
query-source-v6 address *;\n\
notify-source *;\n\
notify-source-v6 *;\n\
cleaning-interval 0; /* now meaningless */\n\
# min-roots <obsolete>;\n\
lame-ttl 600;\n\
servfail-ttl 1;\n\
max-ncache-ttl 10800; /* 3 hours */\n\
max-cache-ttl 604800; /* 1 week */\n\
transfer-format many-answers;\n\
max-cache-size 90%;\n\
check-names master fail;\n\
check-names slave warn;\n\
check-names response ignore;\n\
check-dup-records warn;\n\ check-dup-records warn;\n\
check-mx warn;\n\ check-mx warn;\n\
check-names master fail;\n\
check-names response ignore;\n\
check-names slave warn;\n\
check-spf warn;\n\ check-spf warn;\n\
cleaning-interval 0; /* now meaningless */\n\
clients-per-query 10;\n\
dnssec-accept-expired no;\n\
dnssec-enable yes;\n\ dnssec-enable yes;\n\
dnssec-validation yes; \n\ dnssec-validation yes; \n\
dnssec-accept-expired no;\n\ # fetch-glue <obsolete>;\n\
fetches-per-zone 0;\n\
fetch-quota-params 100 0.1 0.3 0.7;\n\ fetch-quota-params 100 0.1 0.3 0.7;\n\
clients-per-query 10;\n\ fetches-per-server 0;\n\
max-clients-per-query 100;\n\ fetches-per-zone 0;\n\
max-recursion-depth 7;\n\ "
max-recursion-queries 75;\n\ #ifdef ALLOW_FILTER_AAAA
zero-no-soa-ttl-cache no;\n\ " filter-aaaa-on-v4 no;\n\
nsec3-test-zone no;\n\ filter-aaaa-on-v6 no;\n\
allow-new-zones no;\n\ filter-aaaa { any; };\n\
"
#endif
"\
glue-cache yes;\n\
lame-ttl 600;\n\
" "
#ifdef HAVE_LMDB #ifdef HAVE_LMDB
"\ "\
@ -184,10 +169,34 @@ options {\n\
" "
#endif #endif
"\ "\
fetches-per-server 0;\n\ max-cache-size 90%;\n\
require-server-cookie no;\n\ max-cache-ttl 604800; /* 1 week */\n\
v6-bias 50;\n\ max-clients-per-query 100;\n\
max-ncache-ttl 10800; /* 3 hours */\n\
max-recursion-depth 7;\n\
max-recursion-queries 75;\n\
message-compression yes;\n\ message-compression yes;\n\
# min-roots <obsolete>;\n\
minimal-any false;\n\
minimal-responses true;\n\
notify-source *;\n\
notify-source-v6 *;\n\
nsec3-test-zone no;\n\
provide-ixfr true;\n\
query-source address *;\n\
query-source-v6 address *;\n\
recursion true;\n\
request-expire true;\n\
request-ixfr true;\n\
require-server-cookie no;\n\
# rfc2308-type1 <obsolete>;\n\
servfail-ttl 1;\n\
# sortlist <none>\n\
synth-from-dnssec yes;\n\
# topology <none>\n\
transfer-format many-answers;\n\
v6-bias 50;\n\
zero-no-soa-ttl-cache no;\n\
" "
#ifdef HAVE_DNSTAP #ifdef HAVE_DNSTAP
"\ "\
@ -199,12 +208,6 @@ options {\n\
geoip-use-ecs yes;\n\ geoip-use-ecs yes;\n\
" "
#endif #endif
#ifdef ALLOW_FILTER_AAAA
" filter-aaaa-on-v4 no;\n\
filter-aaaa-on-v6 no;\n\
filter-aaaa { any; };\n\
"
#endif
" /* zone */\n\ " /* zone */\n\
allow-query {any;};\n\ allow-query {any;};\n\

View File

@ -71,7 +71,6 @@ struct ns_query {
isc_boolean_t authoritative; isc_boolean_t authoritative;
isc_boolean_t is_zone; isc_boolean_t is_zone;
} redirect; } redirect;
}; };
#define NS_QUERYATTR_RECURSIONOK 0x0001 #define NS_QUERYATTR_RECURSIONOK 0x0001

View File

@ -205,7 +205,11 @@ enum {
dns_nsstatscounter_cookienew = 56, dns_nsstatscounter_cookienew = 56,
dns_nsstatscounter_badcookie = 57, dns_nsstatscounter_badcookie = 57,
dns_nsstatscounter_max = 58 dns_nsstatscounter_nxdomainsynth = 58,
dns_nsstatscounter_nodatasynth = 59,
dns_nsstatscounter_wildcardsynth = 60,
dns_nsstatscounter_max = 61
}; };
/*% /*%

View File

@ -435,6 +435,7 @@ options {
stacksize ( default | unlimited | <replaceable>sizeval</replaceable> ); stacksize ( default | unlimited | <replaceable>sizeval</replaceable> );
startup-notify-rate <replaceable>integer</replaceable>; startup-notify-rate <replaceable>integer</replaceable>;
statistics-file <replaceable>quoted_string</replaceable>; statistics-file <replaceable>quoted_string</replaceable>;
synth-from-dnssec <replaceable>boolean</replaceable>;
tcp-advertised-timeout <replaceable>integer</replaceable>; tcp-advertised-timeout <replaceable>integer</replaceable>;
tcp-clients <replaceable>integer</replaceable>; tcp-clients <replaceable>integer</replaceable>;
tcp-idle-timeout <replaceable>integer</replaceable>; tcp-idle-timeout <replaceable>integer</replaceable>;
@ -769,6 +770,7 @@ view <replaceable>string</replaceable> <optional> <replaceable>class</replaceabl
sig-signing-type <replaceable>integer</replaceable>; sig-signing-type <replaceable>integer</replaceable>;
sig-validity-interval <replaceable>integer</replaceable> <optional> <replaceable>integer</replaceable> </optional>; sig-validity-interval <replaceable>integer</replaceable> <optional> <replaceable>integer</replaceable> </optional>;
sortlist { <replaceable>address_match_element</replaceable>; ... }; sortlist { <replaceable>address_match_element</replaceable>; ... };
synth-from-dnssec <replaceable>boolean</replaceable>;
transfer-format ( many-answers | one-answer ); transfer-format ( many-answers | one-answer );
transfer-source ( <replaceable>ipv4_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional> <optional> transfer-source ( <replaceable>ipv4_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional> <optional>
dscp <replaceable>integer</replaceable> </optional>; dscp <replaceable>integer</replaceable> </optional>;

View File

@ -33,6 +33,7 @@
#include <dns/events.h> #include <dns/events.h>
#include <dns/message.h> #include <dns/message.h>
#include <dns/ncache.h> #include <dns/ncache.h>
#include <dns/nsec.h>
#include <dns/nsec3.h> #include <dns/nsec3.h>
#include <dns/order.h> #include <dns/order.h>
#include <dns/rdata.h> #include <dns/rdata.h>
@ -231,6 +232,10 @@ static isc_boolean_t
rpz_ck_dnssec(ns_client_t *client, isc_result_t qresult, rpz_ck_dnssec(ns_client_t *client, isc_result_t qresult,
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset); dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
static void
log_noexistnodata(void *val, int level, const char *fmt, ...)
ISC_FORMAT_PRINTF(3, 4);
/*% /*%
* The structure and functions defined below implement the query logic * The structure and functions defined below implement the query logic
* that previously lived in the single very complex function query_find(). * that previously lived in the single very complex function query_find().
@ -320,6 +325,7 @@ typedef struct query_ctx {
* restart needed */ * restart needed */
isc_boolean_t need_wildcardproof; /* wilcard proof needed */ isc_boolean_t need_wildcardproof; /* wilcard proof needed */
isc_boolean_t nxrewrite; /* negative answer from RPZ */ isc_boolean_t nxrewrite; /* negative answer from RPZ */
isc_boolean_t findcoveringnsec; /* lookup covering NSEC */
dns_fixedname_t wildcardname; /* name needing wcard proof */ dns_fixedname_t wildcardname; /* name needing wcard proof */
dns_fixedname_t dsname; /* name needing DS */ dns_fixedname_t dsname; /* name needing DS */
@ -430,6 +436,9 @@ query_redirect(query_ctx_t *qctx);
static isc_result_t static isc_result_t
query_ncache(query_ctx_t *qctx, isc_result_t result); query_ncache(query_ctx_t *qctx, isc_result_t result);
static isc_result_t
query_coveringnsec(query_ctx_t *qctx);
static isc_result_t static isc_result_t
query_cname(query_ctx_t *qctx); query_cname(query_ctx_t *qctx);
@ -4268,7 +4277,8 @@ dns64_aaaaok(ns_client_t *client, dns_rdataset_t *rdataset,
if (RECURSIONOK(client)) if (RECURSIONOK(client))
flags |= DNS_DNS64_RECURSIVE; flags |= DNS_DNS64_RECURSIVE;
if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) if (WANTDNSSEC(client) && sigrdataset != NULL &&
dns_rdataset_isassociated(sigrdataset))
flags |= DNS_DNS64_DNSSEC; flags |= DNS_DNS64_DNSSEC;
count = dns_rdataset_count(rdataset); count = dns_rdataset_count(rdataset);
@ -4621,6 +4631,7 @@ qctx_init(ns_client_t *client, dns_fetchevent_t *event,
qctx->options = 0; qctx->options = 0;
qctx->resuming = ISC_FALSE; qctx->resuming = ISC_FALSE;
qctx->is_zone = ISC_FALSE; qctx->is_zone = ISC_FALSE;
qctx->findcoveringnsec = client->view->synthfromdnssec;
qctx->is_staticstub_zone = ISC_FALSE; qctx->is_staticstub_zone = ISC_FALSE;
qctx->nxrewrite = ISC_FALSE; qctx->nxrewrite = ISC_FALSE;
qctx->authoritative = ISC_FALSE; qctx->authoritative = ISC_FALSE;
@ -4920,6 +4931,7 @@ query_lookup(query_ctx_t *qctx) {
dns_clientinfomethods_t cm; dns_clientinfomethods_t cm;
dns_clientinfo_t ci; dns_clientinfo_t ci;
dns_name_t *rpzqname = NULL; dns_name_t *rpzqname = NULL;
unsigned int dboptions;
CCTRACE(ISC_LOG_DEBUG(3), "query_lookup"); CCTRACE(ISC_LOG_DEBUG(3), "query_lookup");
@ -4947,7 +4959,7 @@ query_lookup(query_ctx_t *qctx) {
return (query_done(qctx)); return (query_done(qctx));
} }
if (WANTDNSSEC(qctx->client) && if ((WANTDNSSEC(qctx->client) || qctx->findcoveringnsec) &&
(!qctx->is_zone || dns_db_issecure(qctx->db))) (!qctx->is_zone || dns_db_issecure(qctx->db)))
{ {
qctx->sigrdataset = query_newrdataset(qctx->client); qctx->sigrdataset = query_newrdataset(qctx->client);
@ -4968,10 +4980,12 @@ query_lookup(query_ctx_t *qctx) {
rpzqname = qctx->client->query.qname; rpzqname = qctx->client->query.qname;
} }
result = dns_db_findext(qctx->db, rpzqname, dboptions = qctx->client->query.dboptions;
qctx->version, qctx->type, if (!qctx->is_zone && qctx->findcoveringnsec)
qctx->client->query.dboptions, dboptions |= DNS_DBFIND_COVERINGNSEC;
qctx->client->now, &qctx->node,
result = dns_db_findext(qctx->db, rpzqname, qctx->version, qctx->type,
dboptions, qctx->client->now, &qctx->node,
qctx->fname, &cm, &ci, qctx->fname, &cm, &ci,
qctx->rdataset, qctx->sigrdataset); qctx->rdataset, qctx->sigrdataset);
@ -5955,6 +5969,9 @@ query_gotanswer(query_ctx_t *qctx, isc_result_t result) {
case DNS_R_NXDOMAIN: case DNS_R_NXDOMAIN:
return (query_nxdomain(qctx, ISC_FALSE)); return (query_nxdomain(qctx, ISC_FALSE));
case DNS_R_COVERINGNSEC:
return (query_coveringnsec(qctx));
case DNS_R_NCACHENXDOMAIN: case DNS_R_NCACHENXDOMAIN:
result = query_redirect(qctx); result = query_redirect(qctx);
if (result != ISC_R_COMPLETE) if (result != ISC_R_COMPLETE)
@ -6507,7 +6524,7 @@ query_respond(query_ctx_t *qctx) {
return (query_lookup(qctx)); return (query_lookup(qctx));
} }
if (qctx->sigrdataset != NULL) { if (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL) {
sigrdatasetp = &qctx->sigrdataset; sigrdatasetp = &qctx->sigrdataset;
} }
@ -6688,7 +6705,7 @@ query_dns64(query_ctx_t *qctx) {
* We use the signatures from the A lookup to set DNS_DNS64_DNSSEC * We use the signatures from the A lookup to set DNS_DNS64_DNSSEC
* as this provides a easy way to see if the answer was signed. * as this provides a easy way to see if the answer was signed.
*/ */
if (qctx->sigrdataset != NULL && if (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL &&
dns_rdataset_isassociated(qctx->sigrdataset)) dns_rdataset_isassociated(qctx->sigrdataset))
flags |= DNS_DNS64_DNSSEC; flags |= DNS_DNS64_DNSSEC;
@ -7098,7 +7115,7 @@ query_zone_delegation(query_ctx_t *qctx) {
*/ */
qctx->client->query.attributes &= qctx->client->query.attributes &=
~NS_QUERYATTR_NOADDITIONAL; ~NS_QUERYATTR_NOADDITIONAL;
if (qctx->sigrdataset != NULL) if (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL)
sigrdatasetp = &qctx->sigrdataset; sigrdatasetp = &qctx->sigrdataset;
query_addrrset(qctx->client, &qctx->fname, query_addrrset(qctx->client, &qctx->fname,
&qctx->rdataset, sigrdatasetp, &qctx->rdataset, sigrdatasetp,
@ -7262,7 +7279,7 @@ query_delegation(query_ctx_t *qctx) {
* delegations. * delegations.
*/ */
qctx->client->query.attributes &= ~NS_QUERYATTR_NOADDITIONAL; qctx->client->query.attributes &= ~NS_QUERYATTR_NOADDITIONAL;
if (qctx->sigrdataset != NULL) { if (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL) {
sigrdatasetp = &qctx->sigrdataset; sigrdatasetp = &qctx->sigrdataset;
} }
query_addrrset(qctx->client, &qctx->fname, query_addrrset(qctx->client, &qctx->fname,
@ -7869,6 +7886,344 @@ query_redirect(query_ctx_t *qctx) {
return (ISC_R_COMPLETE); return (ISC_R_COMPLETE);
} }
/*%
* Logging function to be passed to dns_nsec_noexistnodata.
*/
static void
log_noexistnodata(void *val, int level, const char *fmt, ...) {
query_ctx_t *qctx = val;
va_list ap;
va_start(ap, fmt);
ns_client_logv(qctx->client, NS_LOGCATEGORY_QUERIES,
NS_LOGMODULE_QUERY, level, fmt, ap);
va_end(ap);
}
/*%
* Handle covering NSEC responses.
*
* Verify the NSEC record is apropriate for the QNAME, if not
* redo the initial query without DNS_DBFIND_COVERINGNSEC.
*
* Compute the wildcard record and check if the wildcard name
* exists or not. If we can't determine this redo the initial
* query without DNS_DBFIND_COVERINGNSEC.
*
* If the wildcard name does not exist compute the SOA name and look
* that up. If the SOA record does not exist redo the initial query
* without DNS_DBFIND_COVERINGNSEC. If the SOA record exists constructed
* a NXDOMAIN response from the found records.
*
* If the wildcard name does exist perform a lookup for the requested
* type at the wildcard name.
*/
static isc_result_t
query_coveringnsec(query_ctx_t *qctx) {
dns_db_t *db = NULL;
dns_clientinfo_t ci;
dns_clientinfomethods_t cm;
dns_dbnode_t *node = NULL;
dns_fixedname_t fixed;
dns_fixedname_t fnowild;
dns_fixedname_t fsigner;
dns_fixedname_t fwild;
dns_name_t *fname = NULL;
dns_name_t *name = NULL;
dns_name_t *nowild = NULL;
dns_name_t *signer = NULL;
dns_name_t *wild = NULL;
dns_rdataset_t **sigsoardatasetp = NULL;
dns_rdataset_t *clone = NULL, *sigclone = NULL;
dns_rdataset_t *soardataset = NULL, *sigsoardataset = NULL;
dns_rdataset_t rdataset, sigrdataset;
dns_ttl_t ttl;
isc_boolean_t done = ISC_FALSE;
isc_boolean_t exists = ISC_TRUE, data = ISC_TRUE;
isc_boolean_t redirected = ISC_FALSE;
isc_buffer_t *dbuf = NULL, b;
isc_result_t result;
unsigned int dboptions = qctx->client->query.dboptions;
/*
* If we have no signer name, stop immediately.
*/
if (!dns_rdataset_isassociated(qctx->sigrdataset)) {
goto cleanup;
}
dns_fixedname_init(&fwild);
wild = dns_fixedname_name(&fwild);
dns_fixedname_init(&fixed);
fname = dns_fixedname_name(&fixed);
dns_fixedname_init(&fsigner);
signer = dns_fixedname_name(&fsigner);
dns_fixedname_init(&fnowild);
nowild = dns_fixedname_name(&fnowild);
dns_rdataset_init(&rdataset);
dns_rdataset_init(&sigrdataset);
dns_clientinfomethods_init(&cm, ns_client_sourceip);
dns_clientinfo_init(&ci, qctx->client, NULL);
/*
* All signer names must be the same to accept.
*/
for (result = dns_rdataset_first(qctx->sigrdataset);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(qctx->sigrdataset))
{
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdata_rrsig_t rrsig;
dns_rdataset_current(qctx->sigrdataset, &rdata);
result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
if (dns_name_countlabels(signer) == 0) {
dns_name_copy(&rrsig.signer, signer, NULL);
} else if (!dns_name_equal(signer, &rrsig.signer)) {
goto cleanup;
}
}
/*
* Check that we have the correct NOQNAME NSEC record.
*/
result = dns_nsec_noexistnodata(qctx->qtype, qctx->client->query.qname,
qctx->fname, qctx->rdataset,
&exists, &data, wild,
log_noexistnodata, qctx);
if (result != ISC_R_SUCCESS || exists) {
goto cleanup;
}
/*
* Look up the no-wildcard proof.
*/
dns_db_attach(qctx->db, &db);
result = dns_db_findext(db, wild, qctx->version, qctx->type,
dboptions | DNS_DBFIND_COVERINGNSEC,
qctx->client->now, &node, nowild,
&cm, &ci, &rdataset, &sigrdataset);
if (rdataset.trust != dns_trust_secure ||
sigrdataset.trust != dns_trust_secure)
{
goto cleanup;
}
switch (result) {
case DNS_R_COVERINGNSEC:
result = dns_nsec_noexistnodata(qctx->qtype, wild,
nowild, &rdataset,
&exists, &data, NULL,
log_noexistnodata, qctx);
if (result != ISC_R_SUCCESS || exists)
goto cleanup;
break;
case ISC_R_SUCCESS: /* wild card match */
case DNS_R_CNAME: /* wild card cname */
case DNS_R_NCACHENXRRSET: /* wild card nodata */
case DNS_R_NCACHENXDOMAIN: /* direct nxdomain */
default:
goto cleanup;
}
/*
* We now have the proof that we have an NXDOMAIN. Apply
* NXDOMAIN redirection if configured.
*/
result = query_redirect(qctx);
if (result != ISC_R_COMPLETE) {
redirected = ISC_TRUE;
goto cleanup;
}
/*
* All signer names must be the same to accept.
*/
if (!dns_rdataset_isassociated(&sigrdataset)) {
goto cleanup;
}
for (result = dns_rdataset_first(&sigrdataset);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(&sigrdataset)) {
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdata_rrsig_t rrsig;
dns_rdataset_current(&sigrdataset, &rdata);
result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
if (dns_name_countlabels(signer) == 0) {
dns_name_copy(&rrsig.signer, signer, NULL);
} else if (!dns_name_equal(signer, &rrsig.signer)) {
goto cleanup;
}
}
if (node != NULL) {
dns_db_detachnode(db, &node);
}
soardataset = query_newrdataset(qctx->client);
sigsoardataset = query_newrdataset(qctx->client);
if (soardataset == NULL || sigsoardataset == NULL) {
goto cleanup;
}
/*
* Look for SOA record to construct NXDOMAIN response.
*/
result = dns_db_findext(db, signer, qctx->version,
dns_rdatatype_soa, dboptions,
qctx->client->now, &node,
fname, &cm, &ci, soardataset,
sigsoardataset);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
qctx->client->message->rcode = dns_rcode_nxdomain;
/*
* Detemine the correct TTL to use for the SOA and RRSIG
*/
ttl = ISC_MIN(qctx->rdataset->ttl, qctx->sigrdataset->ttl);
ttl = ISC_MIN(ttl, rdataset.ttl);
ttl = ISC_MIN(ttl, sigrdataset.ttl);
ttl = ISC_MIN(ttl, soardataset->ttl);
ttl = ISC_MIN(ttl, sigsoardataset->ttl);
soardataset->ttl = sigsoardataset->ttl = ttl;
/*
* We want the SOA record to be first, so save the
* NOQNAME proof's name now or else discard it.
*/
if (WANTDNSSEC(qctx->client)) {
query_keepname(qctx->client, qctx->fname, qctx->dbuf);
} else {
query_releasename(qctx->client, &qctx->fname);
}
dbuf = query_getnamebuf(qctx->client);
if (dbuf == NULL) {
goto cleanup;
}
name = query_newname(qctx->client, dbuf, &b);
if (name == NULL) {
goto cleanup;
}
dns_name_clone(signer, name);
/*
* Add SOA record. Omit the RRSIG if DNSSEC was not requested.
*/
if (WANTDNSSEC(qctx->client)) {
sigsoardatasetp = &sigsoardataset;
}
query_addrrset(qctx->client, &name, &soardataset, sigsoardatasetp,
dbuf, DNS_SECTION_AUTHORITY);
if (WANTDNSSEC(qctx->client)) {
/*
* Add NODATA proof.
*/
query_addrrset(qctx->client, &qctx->fname,
&qctx->rdataset, &qctx->sigrdataset,
NULL, DNS_SECTION_AUTHORITY);
dbuf = query_getnamebuf(qctx->client);
if (dbuf == NULL) {
goto cleanup;
}
name = query_newname(qctx->client, dbuf, &b);
if (name == NULL) {
goto cleanup;
}
dns_name_clone(nowild, name);
clone = query_newrdataset(qctx->client);
sigclone = query_newrdataset(qctx->client);
if (clone == NULL || sigclone == NULL) {
goto cleanup;
}
dns_rdataset_clone(&rdataset, clone);
dns_rdataset_clone(&sigrdataset, sigclone);
/*
* Add NOWILDCARD proof.
*/
query_addrrset(qctx->client, &name, &clone, &sigclone,
dbuf, DNS_SECTION_AUTHORITY);
}
inc_stats(qctx->client, dns_nsstatscounter_nxdomainsynth);
done = ISC_TRUE;
cleanup:
if (clone != NULL) {
query_putrdataset(qctx->client, &clone);
}
if (sigclone != NULL) {
query_putrdataset(qctx->client, &sigclone);
}
if (dns_rdataset_isassociated(&rdataset)) {
dns_rdataset_disassociate(&rdataset);
}
if (dns_rdataset_isassociated(&sigrdataset)) {
dns_rdataset_disassociate(&sigrdataset);
}
if (soardataset != NULL) {
query_putrdataset(qctx->client, &soardataset);
}
if (sigsoardataset != NULL) {
query_putrdataset(qctx->client, &sigsoardataset);
}
if (db != NULL) {
if (node != NULL) {
dns_db_detachnode(db, &node);
}
dns_db_detach(&db);
}
if (name != NULL) {
query_releasename(qctx->client, &name);
}
if (redirected) {
return (result);
}
if (!done) {
/*
* No covering NSEC was found; proceed with recursion.
*/
qctx->findcoveringnsec = ISC_FALSE;
if (qctx->fname != NULL) {
query_releasename(qctx->client, &qctx->fname);
}
if (qctx->node != NULL) {
dns_db_detachnode(qctx->db, &qctx->node);
}
query_putrdataset(qctx->client, &qctx->rdataset);
if (qctx->sigrdataset != NULL) {
query_putrdataset(qctx->client, &qctx->sigrdataset);
}
return (query_lookup(qctx));
}
return (query_done(qctx));
}
/*% /*%
* Handle negative cache responses, DNS_R_NCACHENXRRSET or * Handle negative cache responses, DNS_R_NCACHENXRRSET or
* DNS_R_NCACHENXDOMAIN. (Note: may also be called with result * DNS_R_NCACHENXDOMAIN. (Note: may also be called with result
@ -7955,7 +8310,7 @@ query_cname(query_ctx_t *qctx) {
/* /*
* Add the CNAME to the answer section. * Add the CNAME to the answer section.
*/ */
if (qctx->sigrdataset != NULL) if (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL)
sigrdatasetp = &qctx->sigrdataset; sigrdatasetp = &qctx->sigrdataset;
if (WANTDNSSEC(qctx->client) && if (WANTDNSSEC(qctx->client) &&
@ -8066,7 +8421,7 @@ query_dname(query_ctx_t *qctx) {
/* /*
* Add the DNAME to the answer section. * Add the DNAME to the answer section.
*/ */
if (qctx->sigrdataset != NULL) if (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL)
sigrdatasetp = &qctx->sigrdataset; sigrdatasetp = &qctx->sigrdataset;
if (WANTDNSSEC(qctx->client) && if (WANTDNSSEC(qctx->client) &&

View File

@ -3733,6 +3733,11 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
if (view->maxncachettl > 7 * 24 * 3600) if (view->maxncachettl > 7 * 24 * 3600)
view->maxncachettl = 7 * 24 * 3600; view->maxncachettl = 7 * 24 * 3600;
obj = NULL;
result = ns_config_get(maps, "synth-from-dnssec", &obj);
INSIST(result == ISC_R_SUCCESS);
view->synthfromdnssec = cfg_obj_asboolean(obj);
/* /*
* Configure the view's cache. * Configure the view's cache.
* *

View File

@ -290,6 +290,9 @@ init_desc(void) {
"resulted in a successful remote lookup", "resulted in a successful remote lookup",
"QryNXRedirRLookup"); "QryNXRedirRLookup");
SET_NSSTATDESC(badcookie, "sent badcookie response", "QryBADCOOKIE"); SET_NSSTATDESC(badcookie, "sent badcookie response", "QryBADCOOKIE");
SET_NSSTATDESC(nxdomainsynth, "synthesized a NXDOMAIN response", "SynthNXDOMAIN");
SET_NSSTATDESC(nodatasynth, "syththesized a no-data response", "SynthNODATA");
SET_NSSTATDESC(wildcardsynth, "synthesized a wildcard response", "SynthWILDCARD");
INSIST(i == dns_nsstatscounter_max); INSIST(i == dns_nsstatscounter_max);
/* Initialize resolver statistics */ /* Initialize resolver statistics */

View File

@ -61,8 +61,8 @@ options {
serial-query-rate 100; serial-query-rate 100;
server-id none; server-id none;
max-cache-size 20000000000000; max-cache-size 20000000000000;
nta-recheck 604800;
nta-lifetime 604800; nta-lifetime 604800;
nta-recheck 604800;
transfer-source 0.0.0.0 dscp 63; transfer-source 0.0.0.0 dscp 63;
zone-statistics none; zone-statistics none;
}; };

View File

@ -84,8 +84,9 @@ SUBDIRS="acl additional addzone allow_query autosign builtin
pipelined @PKCS11_TEST@ reclimit redirect resolver rndc pipelined @PKCS11_TEST@ reclimit redirect resolver rndc
rpz rpzrecurse rrchecker rrl rrsetorder rsabigexponent rpz rpzrecurse rrchecker rrl rrsetorder rsabigexponent
runtime sfcache smartsign sortlist spf staticstub statistics runtime sfcache smartsign sortlist spf staticstub statistics
statschannel stub tcp tkey tools tsig tsiggss unknown upforwd statschannel stub synthfromdnssec tcp tkey tools tsig
verify views wildcard xfer xferquota zero zonechecks" tsiggss unknown upforwd verify views wildcard xfer xferquota
zero zonechecks"
# Things that are different on Windows # Things that are different on Windows
KILL=kill KILL=kill

View File

@ -1754,7 +1754,7 @@ ret=0
$PERL -e 'my $delay = '$start' + 11 - time(); select(undef, undef, undef, $delay) if ($delay > 0);' $PERL -e 'my $delay = '$start' + 11 - time(); select(undef, undef, undef, $delay) if ($delay > 0);'
# check nta table # check nta table
$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -d > rndc.out.ns4.test$n._11 $RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -d > rndc.out.ns4.test$n._11
lines=`wc -l < rndc.out.ns4.test$n._11` lines=`grep " expiry " rndc.out.ns4.test$n._11 | wc -l`
[ "$lines" -le 2 ] || ret=1 [ "$lines" -le 2 ] || ret=1
grep "bogus.example: expiry" rndc.out.ns4.test$n._11 > /dev/null || ret=1 grep "bogus.example: expiry" rndc.out.ns4.test$n._11 > /dev/null || ret=1
grep "badds.example: expiry" rndc.out.ns4.test$n._11 > /dev/null && ret=1 grep "badds.example: expiry" rndc.out.ns4.test$n._11 > /dev/null && ret=1
@ -1783,7 +1783,7 @@ $DIG $DIGOPTS c.bogus.example. a @10.53.0.4 > dig.out.ns4.test$n.15 || ret=1
grep "status: SERVFAIL" dig.out.ns4.test$n.15 > /dev/null || ret=1 grep "status: SERVFAIL" dig.out.ns4.test$n.15 > /dev/null || ret=1
# check nta table has been cleaned up now # check nta table has been cleaned up now
$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -d > rndc.out.ns4.test$n.3 $RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -d > rndc.out.ns4.test$n.3
lines=`wc -l < rndc.out.ns4.test$n.3` lines=`grep " expiry " rndc.out.ns4.test$n.3 | wc -l`
[ "$lines" -eq 0 ] || ret=1 [ "$lines" -eq 0 ] || ret=1
n=`expr $n + 1` n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed - checking that all nta's have been lifted"; fi if [ $ret != 0 ]; then echo "I:failed - checking that all nta's have been lifted"; fi
@ -1845,12 +1845,12 @@ ret=0
n=`expr $n + 1` n=`expr $n + 1`
echo "I: testing NTA persistence across restarts ($n)" echo "I: testing NTA persistence across restarts ($n)"
$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -d > rndc.out.ns4.test$n.1 $RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -d > rndc.out.ns4.test$n.1
lines=`wc -l < rndc.out.ns4.test$n.1` lines=`grep " expiry " rndc.out.ns4.test$n.1 | wc -l`
[ "$lines" -eq 0 ] || ret=1 [ "$lines" -eq 0 ] || ret=1
$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -f -l 30s bogus.example 2>&1 | sed 's/^/I:ns4 /' $RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -f -l 30s bogus.example 2>&1 | sed 's/^/I:ns4 /'
$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -f -l 10s badds.example 2>&1 | sed 's/^/I:ns4 /' $RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -f -l 10s badds.example 2>&1 | sed 's/^/I:ns4 /'
$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -d > rndc.out.ns4.test$n.2 $RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -d > rndc.out.ns4.test$n.2
lines=`wc -l < rndc.out.ns4.test$n.2` lines=`grep " expiry " rndc.out.ns4.test$n.2 | wc -l`
[ "$lines" -eq 2 ] || ret=1 [ "$lines" -eq 2 ] || ret=1
start=`$PERL -e 'print time()."\n";'` start=`$PERL -e 'print time()."\n";'`

View File

@ -0,0 +1,10 @@
rm -f dig.out.*
rm -f ns1/K*+*+*.key
rm -f ns1/K*+*+*.private
rm -f ns1/dsset-*
rm -f ns1/example.db
rm -f ns1/example.db.signed
rm -f ns1/root.db
rm -f ns1/root.db.signed
rm -f ns1/trusted.conf
rm -f ns2/named_dump.db

View File

@ -0,0 +1,7 @@
$TTL 3600
@ SOA ns1 hostmaster 1 3600 1200 604800 3600
@ NS ns1
ns1 A 10.53.0.1
nodata TXT nodata
*.wild-a A 1.2.3.4
*.wild-cname CNAME ns1

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// NS1
controls { /* empty */ };
options {
query-source address 10.53.0.1;
notify-source 10.53.0.1;
transfer-source 10.53.0.1;
port 5300;
pid-file "named.pid";
listen-on { 10.53.0.1; };
listen-on-v6 { none; };
recursion no;
notify yes;
dnssec-enable yes;
dnssec-validation yes;
};
zone "." {
type master;
file "root.db.signed";
};
zone "example" {
type master;
file "example.db.signed";
};
include "trusted.conf";

View File

@ -0,0 +1,6 @@
$TTL 3600
@ SOA ns1 hostmaster 1 3600 1200 604800 3600
@ NS ns1
ns1 A 10.53.0.1
example NS ns1.example
ns1.example A 10.53.0.1

View File

@ -0,0 +1,40 @@
#!/bin/sh -e
#
# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
SYSTEMTESTTOP=../..
. $SYSTEMTESTTOP/conf.sh
zone=example
infile=example.db.in
zonefile=example.db
keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA256 -b 2048 -n zone $zone`
cat $infile $keyname.key > $zonefile
$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null
zone=.
infile=root.db.in
zonefile=root.db
keyname=`$KEYGEN -q -r $RANDFILE -a RSAMD5 -b 1024 -n zone $zone`
cat $infile $keyname.key > $zonefile
$SIGNER -P -g -r $RANDFILE -o $zone $zonefile > /dev/null
# Configure the resolving server with a trusted key.
cat $keyname.key | grep -v '^; ' | $PERL -n -e '
local ($dn, $class, $type, $flags, $proto, $alg, @rest) = split;
local $key = join("", @rest);
print <<EOF
trusted-keys {
"$dn" $flags $proto $alg "$key";
};
EOF
' > trusted.conf

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// NS2
controls { /* empty */ };
options {
query-source address 10.53.0.2;
notify-source 10.53.0.2;
transfer-source 10.53.0.2;
port 5300;
pid-file "named.pid";
listen-on { 10.53.0.2; };
listen-on-v6 { none; };
recursion yes;
notify no;
dnssec-enable yes;
dnssec-validation yes;
};
zone "." {
type hint;
file "root.hints";
};
include "../ns1/trusted.conf";
include "../../common/controls.conf";

View File

@ -0,0 +1,2 @@
. NS ns1
ns1 A 10.53.0.1

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// NS3
controls { /* empty */ };
options {
query-source address 10.53.0.3;
notify-source 10.53.0.3;
transfer-source 10.53.0.3;
port 5300;
pid-file "named.pid";
listen-on { 10.53.0.3; };
listen-on-v6 { none; };
recursion yes;
notify no;
dnssec-enable yes;
dnssec-validation yes;
};
zone "." {
type hint;
file "root.hints";
};
zone "." {
type redirect;
file "redirect.db";
};
include "../ns1/trusted.conf";

View File

@ -0,0 +1,17 @@
; Copyright (C) 2011, 2016 Internet Systems Consortium, Inc. ("ISC")
;
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
; $Id: redirect.db,v 1.3 2011/03/01 23:48:06 tbox Exp $
$TTL 300
@ IN SOA ns.example.net hostmaster.example.net 0 0 0 0 0
@ IN NS ns.example.net
;
; NS records do not need address records in this zone as it is not in the
; normal namespace.
;
*.redirect. IN A 100.100.100.2
*.redirect. IN AAAA 2001:ffff:ffff::100.100.100.2

View File

@ -0,0 +1,2 @@
. NS ns1
ns1 A 10.53.0.1

View File

@ -0,0 +1,17 @@
#!/bin/sh -e
#
# Copyright (C) 2000, 2001, 2004, 2007, 2009, 2011-2017 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
$SHELL clean.sh
test -r $RANDFILE || $GENRANDOM 800 $RANDFILE
cd ns1
$SHELL sign.sh

View File

@ -0,0 +1,139 @@
#!/bin/sh
#
# Copyright (C) 2000-2002, 2004-2017 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
status=0
n=1
rm -f dig.out.*
DIGOPTS="+tcp +noadd +nosea +nostat +nocmd +dnssec -p 5300"
DELVOPTS="-a ns1/trusted.conf -p 5300"
echo "I:prime negative NXDOMAIN response ($n)"
ret=0
$DIG $DIGOPTS a.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1
grep "flags:[^;]* ad[ ;]" dig.out.ns2.test$n > /dev/null || ret=1
grep "status: NXDOMAIN," dig.out.ns2.test$n > /dev/null || ret=1
grep "example.*3600.IN.SOA" dig.out.ns2.test$n > /dev/null || ret=1
nxdomain=dig.out.ns2.test$n
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:prime negative NODATA response ($n)"
ret=0
$DIG $DIGOPTS nodata.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1
grep "flags:[^;]* ad[ ;]" dig.out.ns2.test$n > /dev/null || ret=1
grep "status: NOERROR," dig.out.ns2.test$n > /dev/null || ret=1
grep "example.*3600.IN.SOA" dig.out.ns2.test$n > /dev/null || ret=1
nodata=dig.out.ns2.test$n
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:prime wildcard response ($n)"
ret=0
$DIG $DIGOPTS a.wild-a.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1
grep "flags:[^;]* ad[ ;]" dig.out.ns2.test$n > /dev/null || ret=1
grep "status: NOERROR," dig.out.ns2.test$n > /dev/null || ret=1
grep "a.wild-a.example.*3600.IN.A" dig.out.ns2.test$n > /dev/null || ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:prime wildcard CNAME response ($n)"
ret=0
$DIG $DIGOPTS a.wild-cname.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1
grep "flags:[^;]* ad[ ;]" dig.out.ns2.test$n > /dev/null || ret=1
grep "status: NOERROR," dig.out.ns2.test$n > /dev/null || ret=1
grep "a.wild-cname.example.*3600.IN.CNAME" dig.out.ns2.test$n > /dev/null || ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:prime redirect response (+nodnssec) ($n)"
ret=0
$DIG $DIGOPTS +nodnssec a.redirect. @10.53.0.3 a > dig.out.ns2.test$n || ret=1
grep "flags:[^;]* ad[ ;]" dig.out.ns2.test$n > /dev/null && ret=1
grep "status: NOERROR," dig.out.ns2.test$n > /dev/null || ret=1
grep 'a\.redirect\..*300.IN.A.100\.100\.100\.2' dig.out.ns2.test$n > /dev/null || ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
sleep 1
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 dumpdb
echo "I:check synthesized NXDOMAIN response ($n)"
ret=0
$DIG $DIGOPTS b.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1
grep "flags:[^;]* ad[ ;]" dig.out.ns2.test$n > /dev/null || ret=1
grep "status: NXDOMAIN," dig.out.ns2.test$n > /dev/null || ret=1
grep "example.*3600.IN.SOA" dig.out.ns2.test$n > /dev/null && ret=1
$PERL ../digcomp.pl $nxdomain dig.out.ns2.test$n || ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:check synthesized NODATA response ($n)"
ret=0
$DIG $DIGOPTS nodata.example. @10.53.0.2 aaaa > dig.out.ns2.test$n || ret=1
grep "flags:[^;]* ad[ ;]" dig.out.ns2.test$n > /dev/null || ret=1
grep "status: NOERROR," dig.out.ns2.test$n > /dev/null || ret=1
grep "example.*3600.IN.SOA" dig.out.ns2.test$n > /dev/null && ret=1
$PERL ../digcomp.pl $nodata dig.out.ns2.test$n || ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed (ignored - to be supported in the future)"; fi
: status=`expr $status + $ret`
echo "I:check synthesized wildcard response ($n)"
ret=0
$DIG $DIGOPTS b.wild-a.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1
grep "flags:[^;]* ad[ ;]" dig.out.ns2.test$n > /dev/null || ret=1
grep "status: NOERROR," dig.out.ns2.test$n > /dev/null || ret=1
grep "b\.wild-a\.example\..*3600.IN.A" dig.out.ns2.test$n > /dev/null && ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed (ignored - to be supported in the future)"; fi
: status=`expr $status + $ret`
echo "I:check synthesized wildcard CNAME response ($n)"
ret=0
$DIG $DIGOPTS b.wild-cname.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1
grep "flags:[^;]* ad[ ;]" dig.out.ns2.test$n > /dev/null || ret=1
grep "status: NOERROR," dig.out.ns2.test$n > /dev/null || ret=1
grep "b.wild-cname.example.*3600.IN.CNAME" dig.out.ns2.test$n > /dev/null && ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed (ignored - to be supported in the future)"; fi
: status=`expr $status + $ret`
echo "I:check redirect response (+dnssec) ($n)"
ret=0
$DIG $DIGOPTS b.redirect. @10.53.0.3 a > dig.out.ns2.test$n || ret=1
grep "flags:[^;]* ad[ ;]" dig.out.ns2.test$n > /dev/null || ret=1
grep "status: NXDOMAIN," dig.out.ns2.test$n > /dev/null || ret=1
grep "\..*3600.IN.SOA" dig.out.ns2.test$n > /dev/null && ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:check redirect response (+nodnssec) ($n)"
ret=0
$DIG $DIGOPTS +nodnssec b.redirect. @10.53.0.3 a > dig.out.ns2.test$n || ret=1
grep "flags:[^;]* ad[ ;]" dig.out.ns2.test$n > /dev/null && ret=1
grep "status: NOERROR," dig.out.ns2.test$n > /dev/null || ret=1
grep 'b\.redirect\..*300.IN.A.100\.100\.100\.2' dig.out.ns2.test$n > /dev/null || ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:exit status: $status"
[ $status -eq 0 ] || exit 1

View File

@ -1417,7 +1417,7 @@ controls {
<para> <para>
Changes that result from incoming incremental zone transfers are Changes that result from incoming incremental zone transfers are
also also
journalled in a similar way. journaled in a similar way.
</para> </para>
<para> <para>
@ -2143,7 +2143,7 @@ allow-update { !{ !localnets; any; }; key host1-host2. ;};
<para> <para>
Any <filename>keyset</filename> files corresponding to Any <filename>keyset</filename> files corresponding to
secure subzones should be present. The zone signer will secure sub-zones should be present. The zone signer will
generate <literal>NSEC</literal>, <literal>NSEC3</literal> generate <literal>NSEC</literal>, <literal>NSEC3</literal>
and <literal>RRSIG</literal> records for the zone, as and <literal>RRSIG</literal> records for the zone, as
well as <literal>DS</literal> for the child zones if well as <literal>DS</literal> for the child zones if
@ -7313,6 +7313,38 @@ options {
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><command>synth-from-dnssec</command></term>
<listitem>
<para>
Synthesize answers from cached NSEC, NSEC3 and
other RRsets that have been proved to be correct
using DNSSEC. The default is <command>yes</command>.
</para>
<para>
Note:
<itemizedlist>
<listitem>
<para>
DNSSEC validation must be enabled for this
option to be effective.
</para>
</listitem>
<listitem>
<para>
This initial implementation only covers
NXDOMAIN synthesis from NSEC records.
Synthesis of NODATA and wildcard responses
is also planned, as is synthesis from NSEC3
records. All of these will be controlled
by <command>synth-from-dnssec</command>.
</para>
</listitem>
</itemizedlist>
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</section> </section>

View File

@ -392,6 +392,21 @@
"[ECS <replaceable>address/source/scope</replaceable>]". "[ECS <replaceable>address/source/scope</replaceable>]".
</para> </para>
</listitem> </listitem>
<listitem>
<para>
<command>named</command> will now synthesize responses
from cached DNSSEC-verified records. This will reduce
query loads on authoritative servers for signed domains:
if existing cached records can be used to determine
the answer then no query needs to be sent.
</para>
<para>
This behavior is controlled by the new
<filename>named.conf</filename> option
<command>synth-from-dnssec</command>. It is enabled by
default.
</para>
</listitem>
</itemizedlist> </itemizedlist>
</section> </section>

View File

@ -174,9 +174,9 @@ options {
fetches-per-server <integer> [ ( drop | fail ) ]; fetches-per-server <integer> [ ( drop | fail ) ];
fetches-per-zone <integer> [ ( drop | fail ) ]; fetches-per-zone <integer> [ ( drop | fail ) ];
files ( default | unlimited | <sizeval> ); files ( default | unlimited | <sizeval> );
filter-aaaa { <address_match_element>; ... }; // not configured filter-aaaa { <address_match_element>; ... };
filter-aaaa-on-v4 ( break-dnssec | <boolean> ); // not configured filter-aaaa-on-v4 ( break-dnssec | <boolean> );
filter-aaaa-on-v6 ( break-dnssec | <boolean> ); // not configured filter-aaaa-on-v6 ( break-dnssec | <boolean> );
flush-zones-on-shutdown <boolean>; flush-zones-on-shutdown <boolean>;
forward ( first | only ); forward ( first | only );
forwarders [ port <integer> ] [ dscp <integer> ] { ( <ipv4_address> forwarders [ port <integer> ] [ dscp <integer> ] { ( <ipv4_address>
@ -188,8 +188,8 @@ options {
fstrm-set-output-queue-model ( mpsc | spsc ); // not configured fstrm-set-output-queue-model ( mpsc | spsc ); // not configured
fstrm-set-output-queue-size <integer>; // not configured fstrm-set-output-queue-size <integer>; // not configured
fstrm-set-reopen-interval <integer>; // not configured fstrm-set-reopen-interval <integer>; // not configured
geoip-directory ( <quoted_string> | none ); // not configured geoip-directory ( <quoted_string> | none );
geoip-use-ecs <boolean>; // not configured geoip-use-ecs <boolean>;
glue-cache <boolean>; glue-cache <boolean>;
has-old-clients <boolean>; // obsolete has-old-clients <boolean>; // obsolete
heartbeat-interval <integer>; heartbeat-interval <integer>;
@ -208,7 +208,7 @@ options {
listen-on-v6 [ port <integer> ] [ dscp listen-on-v6 [ port <integer> ] [ dscp
<integer> ] { <integer> ] {
<address_match_element>; ... }; // may occur multiple times <address_match_element>; ... }; // may occur multiple times
lmdb-mapsize <sizeval>; // non-operational lmdb-mapsize <sizeval>;
lock-file ( <quoted_string> | none ); lock-file ( <quoted_string> | none );
maintain-ixfr-base <boolean>; // obsolete maintain-ixfr-base <boolean>; // obsolete
managed-keys-directory <quoted_string>; managed-keys-directory <quoted_string>;
@ -338,6 +338,7 @@ options {
statistics-file <quoted_string>; statistics-file <quoted_string>;
statistics-interval <integer>; // not yet implemented statistics-interval <integer>; // not yet implemented
suppress-initial-notify <boolean>; // not yet implemented suppress-initial-notify <boolean>; // not yet implemented
synth-from-dnssec <boolean>;
tcp-advertised-timeout <integer>; tcp-advertised-timeout <integer>;
tcp-clients <integer>; tcp-clients <integer>;
tcp-idle-timeout <integer>; tcp-idle-timeout <integer>;
@ -515,9 +516,9 @@ view <string> [ <class> ] {
fetch-quota-params <integer> <fixedpoint> <fixedpoint> <fixedpoint>; fetch-quota-params <integer> <fixedpoint> <fixedpoint> <fixedpoint>;
fetches-per-server <integer> [ ( drop | fail ) ]; fetches-per-server <integer> [ ( drop | fail ) ];
fetches-per-zone <integer> [ ( drop | fail ) ]; fetches-per-zone <integer> [ ( drop | fail ) ];
filter-aaaa { <address_match_element>; ... }; // not configured filter-aaaa { <address_match_element>; ... };
filter-aaaa-on-v4 ( break-dnssec | <boolean> ); // not configured filter-aaaa-on-v4 ( break-dnssec | <boolean> );
filter-aaaa-on-v6 ( break-dnssec | <boolean> ); // not configured filter-aaaa-on-v6 ( break-dnssec | <boolean> );
forward ( first | only ); forward ( first | only );
forwarders [ port <integer> ] [ dscp <integer> ] { ( <ipv4_address> forwarders [ port <integer> ] [ dscp <integer> ] { ( <ipv4_address>
| <ipv6_address> ) [ port <integer> ] [ dscp <integer> ]; ... }; | <ipv6_address> ) [ port <integer> ] [ dscp <integer> ]; ... };
@ -530,7 +531,7 @@ view <string> [ <class> ] {
}; // may occur multiple times }; // may occur multiple times
key-directory <quoted_string>; key-directory <quoted_string>;
lame-ttl <ttlval>; lame-ttl <ttlval>;
lmdb-mapsize <sizeval>; // non-operational lmdb-mapsize <sizeval>;
maintain-ixfr-base <boolean>; // obsolete maintain-ixfr-base <boolean>; // obsolete
managed-keys { <string> <string> managed-keys { <string> <string>
<integer> <integer> <integer> <integer> <integer> <integer>
@ -674,6 +675,7 @@ view <string> [ <class> ] {
sig-validity-interval <integer> [ <integer> ]; sig-validity-interval <integer> [ <integer> ];
sortlist { <address_match_element>; ... }; sortlist { <address_match_element>; ... };
suppress-initial-notify <boolean>; // not yet implemented suppress-initial-notify <boolean>; // not yet implemented
synth-from-dnssec <boolean>;
topology { <address_match_element>; ... }; // not implemented topology { <address_match_element>; ... }; // not implemented
transfer-format ( many-answers | one-answer ); transfer-format ( many-answers | one-answer );
transfer-source ( <ipv4_address> | * ) [ port ( <integer> | * ) ] [ transfer-source ( <ipv4_address> | * ) [ port ( <integer> | * ) ] [

View File

@ -120,6 +120,7 @@ struct dns_view {
isc_boolean_t enablevalidation; isc_boolean_t enablevalidation;
isc_boolean_t acceptexpired; isc_boolean_t acceptexpired;
isc_boolean_t requireservercookie; isc_boolean_t requireservercookie;
isc_boolean_t synthfromdnssec;
isc_boolean_t trust_anchor_telemetry; isc_boolean_t trust_anchor_telemetry;
dns_transfer_format_t transfer_format; dns_transfer_format_t transfer_format;
dns_acl_t * cacheacl; dns_acl_t * cacheacl;

View File

@ -2629,14 +2629,19 @@ dns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
} }
void void
dns_message_puttempname(dns_message_t *msg, dns_name_t **item) { dns_message_puttempname(dns_message_t *msg, dns_name_t **itemp) {
REQUIRE(DNS_MESSAGE_VALID(msg)); dns_name_t *item;
REQUIRE(item != NULL && *item != NULL);
if (dns_name_dynamic(*item)) REQUIRE(DNS_MESSAGE_VALID(msg));
dns_name_free(*item, msg->mctx); REQUIRE(itemp != NULL && *itemp != NULL);
isc_mempool_put(msg->namepool, *item); item = *itemp;
*item = NULL; REQUIRE(!ISC_LINK_LINKED(item, link));
REQUIRE(ISC_LIST_HEAD(item->list) == NULL);
*itemp = NULL;
if (dns_name_dynamic(item))
dns_name_free(item, msg->mctx);
isc_mempool_put(msg->namepool, item);
} }
void void

View File

@ -4830,7 +4830,8 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
foundsig = NULL; foundsig = NULL;
empty_node = ISC_TRUE; empty_node = ISC_TRUE;
header_prev = NULL; header_prev = NULL;
for (header = node->data; header != NULL; header = header_next) { for (header = node->data; header != NULL; header = header_next)
{
header_next = header->next; header_next = header->next;
if (check_stale_header(node, header, if (check_stale_header(node, header,
&locktype, lock, search, &locktype, lock, search,
@ -4842,7 +4843,15 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
header_prev = header; header_prev = header;
continue; continue;
} }
/*
* Don't stop on provable noqname / RRSIG.
*/
if (header->noqname == NULL &&
RBTDB_RDATATYPE_BASE(header->type)
!= dns_rdatatype_rrsig)
{
empty_node = ISC_FALSE; empty_node = ISC_FALSE;
}
if (header->type == matchtype) if (header->type == matchtype)
found = header; found = header;
else if (header->type == sigmatchtype) else if (header->type == sigmatchtype)
@ -4873,7 +4882,6 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
return (result); return (result);
} }
static isc_result_t static isc_result_t
cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,

View File

@ -5024,6 +5024,8 @@ validated(isc_task_t *task, isc_event_t *event) {
isc_uint32_t ttl; isc_uint32_t ttl;
unsigned options; unsigned options;
isc_uint32_t bucketnum; isc_uint32_t bucketnum;
dns_fixedname_t fwild;
dns_name_t *wild = NULL;
UNUSED(task); /* for now */ UNUSED(task); /* for now */
@ -5048,8 +5050,14 @@ validated(isc_task_t *task, isc_event_t *event) {
/* /*
* Destroy the validator early so that we can * Destroy the validator early so that we can
* destroy the fctx if necessary. * destroy the fctx if necessary. Save the wildcard name.
*/ */
if (vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF] != NULL) {
dns_fixedname_init(&fwild);
wild = dns_fixedname_name(&fwild);
dns_name_copy(dns_fixedname_name(&vevent->validator->wild),
wild, NULL);
}
dns_validator_destroy(&vevent->validator); dns_validator_destroy(&vevent->validator);
isc_mem_put(fctx->mctx, valarg, sizeof(*valarg)); isc_mem_put(fctx->mctx, valarg, sizeof(*valarg));
@ -5324,7 +5332,7 @@ validated(isc_task_t *task, isc_event_t *event) {
answer_response: answer_response:
/* /*
* Cache any NS/NSEC records that happened to be validated. * Cache any SOA/NS/NSEC records that happened to be validated.
*/ */
result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY); result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY);
while (result == ISC_R_SUCCESS) { while (result == ISC_R_SUCCESS) {
@ -5335,6 +5343,7 @@ validated(isc_task_t *task, isc_event_t *event) {
rdataset != NULL; rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) { rdataset = ISC_LIST_NEXT(rdataset, link)) {
if ((rdataset->type != dns_rdatatype_ns && if ((rdataset->type != dns_rdatatype_ns &&
rdataset->type != dns_rdatatype_soa &&
rdataset->type != dns_rdatatype_nsec) || rdataset->type != dns_rdatatype_nsec) ||
rdataset->trust != dns_trust_secure) rdataset->trust != dns_trust_secure)
continue; continue;
@ -5369,6 +5378,37 @@ validated(isc_task_t *task, isc_event_t *event) {
DNS_SECTION_AUTHORITY); DNS_SECTION_AUTHORITY);
} }
/*
* Add the wild card entry.
*/
if (vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF] != NULL &&
vevent->rdataset != NULL &&
dns_rdataset_isassociated(vevent->rdataset) &&
vevent->rdataset->trust == dns_trust_secure &&
vevent->sigrdataset != NULL &&
dns_rdataset_isassociated(vevent->sigrdataset) &&
vevent->sigrdataset->trust == dns_trust_secure &&
wild != NULL)
{
dns_dbnode_t *wnode = NULL;
char namebuf[DNS_NAME_FORMATSIZE];
dns_name_format(wild, namebuf, sizeof(namebuf));
fprintf(stderr, "save wildcard data %s\n", namebuf);
result = dns_db_findnode(fctx->cache, wild, ISC_TRUE, &wnode);
if (result == ISC_R_SUCCESS)
result = dns_db_addrdataset(fctx->cache, wnode, NULL,
now, vevent->rdataset, 0,
NULL);
if (result == ISC_R_SUCCESS)
result = dns_db_addrdataset(fctx->cache, wnode, NULL,
now, vevent->sigrdataset,
0, NULL);
if (wnode != NULL)
dns_db_detachnode(fctx->cache, &wnode);
}
result = ISC_R_SUCCESS; result = ISC_R_SUCCESS;
/* /*

View File

@ -232,6 +232,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
view->requestnsid = ISC_FALSE; view->requestnsid = ISC_FALSE;
view->sendcookie = ISC_TRUE; view->sendcookie = ISC_TRUE;
view->requireservercookie = ISC_FALSE; view->requireservercookie = ISC_FALSE;
view->synthfromdnssec = ISC_TRUE;
view->trust_anchor_telemetry = ISC_TRUE; view->trust_anchor_telemetry = ISC_TRUE;
view->new_zone_dir = NULL; view->new_zone_dir = NULL;
view->new_zone_file = NULL; view->new_zone_file = NULL;

View File

@ -1891,8 +1891,6 @@ view_clauses[] = {
#else #else
{ "lmdb-mapsize", &cfg_type_sizeval, CFG_CLAUSEFLAG_NOOP }, { "lmdb-mapsize", &cfg_type_sizeval, CFG_CLAUSEFLAG_NOOP },
#endif #endif
{ "nocookie-udp-size", &cfg_type_uint32, 0 },
{ "nosit-udp-size", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
{ "max-acache-size", &cfg_type_sizenodefault, { "max-acache-size", &cfg_type_sizenodefault,
CFG_CLAUSEFLAG_OBSOLETE }, CFG_CLAUSEFLAG_OBSOLETE },
{ "max-cache-size", &cfg_type_sizeorpercent, 0 }, { "max-cache-size", &cfg_type_sizeorpercent, 0 },
@ -1902,17 +1900,19 @@ view_clauses[] = {
{ "max-recursion-depth", &cfg_type_uint32, 0 }, { "max-recursion-depth", &cfg_type_uint32, 0 },
{ "max-recursion-queries", &cfg_type_uint32, 0 }, { "max-recursion-queries", &cfg_type_uint32, 0 },
{ "max-udp-size", &cfg_type_uint32, 0 }, { "max-udp-size", &cfg_type_uint32, 0 },
{ "message-compression", &cfg_type_boolean, 0 },
{ "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP }, { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
{ "minimal-any", &cfg_type_boolean, 0 }, { "minimal-any", &cfg_type_boolean, 0 },
{ "minimal-responses", &cfg_type_minimal, 0 }, { "minimal-responses", &cfg_type_minimal, 0 },
{ "new-zones-directory", &cfg_type_qstring, 0 }, { "new-zones-directory", &cfg_type_qstring, 0 },
{ "nta-recheck", &cfg_type_ttlval, 0 },
{ "nta-lifetime", &cfg_type_ttlval, 0 },
{ "nxdomain-redirect", &cfg_type_astring, 0 },
{ "prefetch", &cfg_type_prefetch, 0 },
{ "preferred-glue", &cfg_type_astring, 0 },
{ "no-case-compress", &cfg_type_bracketed_aml, 0 }, { "no-case-compress", &cfg_type_bracketed_aml, 0 },
{ "message-compression", &cfg_type_boolean, 0 }, { "nocookie-udp-size", &cfg_type_uint32, 0 },
{ "nosit-udp-size", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
{ "nta-lifetime", &cfg_type_ttlval, 0 },
{ "nta-recheck", &cfg_type_ttlval, 0 },
{ "nxdomain-redirect", &cfg_type_astring, 0 },
{ "preferred-glue", &cfg_type_astring, 0 },
{ "prefetch", &cfg_type_prefetch, 0 },
{ "provide-ixfr", &cfg_type_boolean, 0 }, { "provide-ixfr", &cfg_type_boolean, 0 },
/* /*
* Note that the query-source option syntax is different * Note that the query-source option syntax is different
@ -1938,6 +1938,7 @@ view_clauses[] = {
{ "servfail-ttl", &cfg_type_ttlval, 0 }, { "servfail-ttl", &cfg_type_ttlval, 0 },
{ "sortlist", &cfg_type_bracketed_aml, 0 }, { "sortlist", &cfg_type_bracketed_aml, 0 },
{ "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI }, { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
{ "synth-from-dnssec", &cfg_type_boolean, 0 },
{ "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP }, { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP },
{ "transfer-format", &cfg_type_transferformat, 0 }, { "transfer-format", &cfg_type_transferformat, 0 },
{ "trust-anchor-telemetry", &cfg_type_boolean, { "trust-anchor-telemetry", &cfg_type_boolean,