if(!ssl_printf(ssl, "%s.num.dnscrypt.malformed"SQ"%lu\n", nm,
(unsigned long)s->svr.num_query_dnscrypt_crypted_malformed)) return 0;
#endif
+ if(!ssl_printf(ssl, "%s.num.dns_error_reports"SQ"%lu\n", nm,
+ (unsigned long)s->svr.num_dns_error_reports)) return 0;
if(!ssl_printf(ssl, "%s.requestlist.avg"SQ"%g\n", nm,
(s->svr.num_queries_missed_cache+s->svr.num_queries_prefetch)?
(double)s->svr.sum_query_list_size/
COPY_VAR_int(serve_expired_reply_ttl);
COPY_VAR_int(serve_expired_client_timeout);
COPY_VAR_int(ede_serve_expired);
+ COPY_VAR_int(dns_error_reporting);
COPY_VAR_int(serve_original_ttl);
COPY_VAR_ptr(val_nsec3_key_iterations);
COPY_VAR_int(zonemd_permissive_mode);
(long long)worker->env.mesh->num_queries_discard_timeout;
s->svr.num_queries_wait_limit +=
(long long)worker->env.mesh->num_queries_wait_limit;
+ s->svr.num_dns_error_reports +=
+ (long long)worker->env.mesh->num_dns_error_reports;
/* values from outside network */
s->svr.unwanted_replies = (long long)worker->back->unwanted_replies;
s->svr.qtcp_outgoing = (long long)worker->back->num_tcp_outgoing;
total->svr.num_queries_discard_timeout +=
a->svr.num_queries_discard_timeout;
total->svr.num_queries_wait_limit += a->svr.num_queries_wait_limit;
+ total->svr.num_dns_error_reports += a->svr.num_dns_error_reports;
total->svr.num_queries_missed_cache += a->svr.num_queries_missed_cache;
total->svr.num_queries_prefetch += a->svr.num_queries_prefetch;
total->svr.num_queries_timed_out += a->svr.num_queries_timed_out;
#ifdef USE_DNSCRYPT
total->svr.num_query_dnscrypt_crypted += a->svr.num_query_dnscrypt_crypted;
total->svr.num_query_dnscrypt_cert += a->svr.num_query_dnscrypt_cert;
- total->svr.num_query_dnscrypt_cleartext += \
+ total->svr.num_query_dnscrypt_cleartext +=
a->svr.num_query_dnscrypt_cleartext;
- total->svr.num_query_dnscrypt_crypted_malformed += \
+ total->svr.num_query_dnscrypt_crypted_malformed +=
a->svr.num_query_dnscrypt_crypted_malformed;
#endif /* USE_DNSCRYPT */
/* the max size reached is upped to higher of both */
# Note that the ede option above needs to be enabled for this to work.
# ede-serve-expired: no
+ # Enable DNS Error Reporting (RFC9567).
+ # qname-minimisation is advised to be turned on as well to increase
+ # privacy on the outgoing reports.
+ # dns-error-reporting: no
+
# Specific options for ipsecmod. Unbound needs to be configured with
# --enable-ipsecmod for these to take effect.
#
.I threadX.num.dnscrypt.malformed
number of request that were neither cleartext, not valid dnscrypt messages.
.TP
+.I threadX.num.dns_error_reports
+number of DNS Error Reports generated by thread
+.TP
.I threadX.num.prefetch
number of cache prefetches performed. This number is included in
cachehits, as the original query had the unprefetched answer from cache,
.I total.num.dnscrypt.malformed
summed over threads.
.TP
+.I total.num.dns_error_reports
+summed over threads.
+.TP
.I total.num.prefetch
summed over threads.
.TP
.TP 5
.B ede: \fI<yes or no>
If enabled, Unbound will respond with Extended DNS Error codes (RFC8914).
-These EDEs attach informative error messages to a response for various
-errors. Default is "no".
+These EDEs provide additional information with a response mainly for, but not
+limited to, DNS and DNSSEC errors.
When the \fBval-log-level\fR option is also set to \fB2\fR, responses with
-Extended DNS Errors concerning DNSSEC failures that are not served from cache,
-will also contain a descriptive text message about the reason for the failure.
+Extended DNS Errors concerning DNSSEC failures will also contain a descriptive
+text message about the reason for the failure.
+Default is "no".
.TP 5
.B ede\-serve\-expired: \fI<yes or no>
If enabled, Unbound will attach an Extended DNS Error (RFC8914) Code 3 - Stale
-Answer as EDNS0 option to the expired response. Note that this will not attach
-the EDE code without setting the global \fBede\fR option to "yes" as well.
+Answer as EDNS0 option to the expired response.
+The \fBede\fR option needs to be enabled as well for this to work.
+Default is "no".
+.TP 5
+.B dns\-error\-reporting: \fI<yes or no>
+If enabled, Unbound will send DNS Error Reports (RFC9567).
+The name servers need to express support by attaching the Report-Channel EDNS0
+option on their replies specifying the reporting agent for the zone.
+Any errors encountered during resolution that would result in Unbound
+generating an Extended DNS Error (RFC8914) will be reported to the zone's
+reporting agent.
+The \fBede\fR option does not need to be enabled for this to work.
+It is advised that the \fBqname\-minimisation\fR option is also enabled to
+increase privacy on the outgoing reports.
Default is "no".
.SS "Remote Control Options"
In the
}
/* Copy the edns options we may got from the back end */
+ qstate->edns_opts_back_in = NULL;
if(edns.opt_list_in) {
qstate->edns_opts_back_in = edns_opt_copy_region(edns.opt_list_in,
qstate->region);
long long num_queries_discard_timeout;
/** number of queries removed due to wait-limit */
long long num_queries_wait_limit;
+ /** number of dns error reports generated */
+ long long num_dns_error_reports;
};
/**
mesh->ans_cachedb = 0;
mesh->num_queries_discard_timeout = 0;
mesh->num_queries_wait_limit = 0;
+ mesh->num_dns_error_reports = 0;
mesh->max_reply_states = env->cfg->num_queries_per_thread;
mesh->max_forever_states = (mesh->max_reply_states+1)/2;
#ifndef S_SPLINT_S
}
}
+/**
+ * Generate the DNS Error Report (RFC9567).
+ * If there is an EDE attached for this reply and there was a Report-Channel
+ * EDNS0 option from the upstream, fire up a report query.
+ * @param qstate: module qstate.
+ * @param rep: prepared reply to be sent.
+ */
+static void dns_error_reporting(struct module_qstate* qstate,
+ struct reply_info* rep)
+{
+ struct query_info qinfo;
+ struct mesh_state* sub;
+ struct module_qstate* newq;
+ uint8_t buf[LDNS_MAX_DOMAINLEN];
+ size_t count = 0;
+ int written;
+ size_t expected_length;
+ struct edns_option* opt;
+ sldns_ede_code reason_bogus = LDNS_EDE_NONE;
+ sldns_rr_type qtype = qstate->qinfo.qtype;
+ uint8_t* qname = qstate->qinfo.qname;
+ size_t qname_len = qstate->qinfo.qname_len-1; /* skip the trailing \0 */
+ uint8_t* agent_domain;
+ size_t agent_domain_len;
+
+ /* We need a valid reporting agent;
+ * this is based on qstate->edns_opts_back_in that will probably have
+ * the latest reporting agent we found while iterating */
+ opt = edns_opt_list_find(qstate->edns_opts_back_in,
+ LDNS_EDNS_REPORT_CHANNEL);
+ if(!opt) return;
+ agent_domain_len = opt->opt_len;
+ agent_domain = opt->opt_data;
+ if(dname_valid(agent_domain, agent_domain_len) < 3) {
+ /* The agent domain needs to be a valid dname that is not the
+ * root; from RFC9567. */
+ return;
+ }
+
+ /* Get the EDE generated from the mesh state, these are mostly
+ * validator errors. If other errors are produced in the future (e.g.,
+ * RPZ) we would not want them to result in error reports. */
+ reason_bogus = errinf_to_reason_bogus(qstate);
+ if(rep && ((reason_bogus == LDNS_EDE_DNSSEC_BOGUS &&
+ rep->reason_bogus != LDNS_EDE_NONE) ||
+ reason_bogus == LDNS_EDE_NONE)) {
+ reason_bogus = rep->reason_bogus;
+ }
+ if(reason_bogus == LDNS_EDE_NONE ||
+ /* other, does not make sense without the text that comes
+ * with it */
+ reason_bogus == LDNS_EDE_OTHER) return;
+
+ /* Synthesize the error report query in the format:
+ * "_er.$qtype.$qname.$ede._er.$reporting-agent-domain" */
+ /* First check if the static length parts fit in the buffer.
+ * That is everything except for qtype and ede that need to be
+ * converted to decimal and checked further on. */
+ expected_length = 4/*_er*/+qname_len+4/*_er*/+agent_domain_len;
+ if(expected_length > LDNS_MAX_DOMAINLEN) goto skip;
+
+ memmove(buf+count, "\3_er", 4);
+ count += 4;
+
+ written = snprintf((char*)buf+count, LDNS_MAX_DOMAINLEN-count,
+ "X%d", qtype);
+ expected_length += written;
+ /* Skip on error, truncation or long expected length */
+ if(written < 0 || (size_t)written >= LDNS_MAX_DOMAINLEN-count ||
+ expected_length > LDNS_MAX_DOMAINLEN ) goto skip;
+ /* Put in the label length */
+ *(buf+count) = (char)(written - 1);
+ count += written;
+
+ memmove(buf+count, qname, qname_len);
+ count += qname_len;
+
+ written = snprintf((char*)buf+count, LDNS_MAX_DOMAINLEN-count,
+ "X%d", reason_bogus);
+ expected_length += written;
+ /* Skip on error, truncation or long expected length */
+ if(written < 0 || (size_t)written >= LDNS_MAX_DOMAINLEN-count ||
+ expected_length > LDNS_MAX_DOMAINLEN ) goto skip;
+ *(buf+count) = (char)(written - 1);
+ count += written;
+
+ memmove(buf+count, "\3_er", 4);
+ count += 4;
+
+ /* Copy the agent domain */
+ memmove(buf+count, agent_domain, agent_domain_len);
+ count += agent_domain_len;
+
+ qinfo.qname = buf;
+ qinfo.qname_len = count;
+ qinfo.qtype = LDNS_RR_TYPE_TXT;
+ qinfo.qclass = qstate->qinfo.qclass;
+ qinfo.local_alias = NULL;
+
+ log_query_info(VERB_ALGO, "DNS Error Reporting: generating report "
+ "query for", &qinfo);
+ if(mesh_add_sub(qstate, &qinfo, BIT_RD, 0, 0, &newq, &sub)) {
+ qstate->env->mesh->num_dns_error_reports++;
+ }
+ return;
+skip:
+ verbose(VERB_ALGO, "DNS Error Reporting: report query qname too long; "
+ "skip");
+ return;
+}
+
void mesh_query_done(struct mesh_state* mstate)
{
struct mesh_reply* r;
if(err) { log_err("%s", err); }
}
}
+
+ if(mstate->reply_list && mstate->s.env->cfg->dns_error_reporting)
+ dns_error_reporting(&mstate->s, rep);
+
for(r = mstate->reply_list; r; r = r->next) {
struct timeval old;
timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time);
mesh->ans_nodata = 0;
mesh->num_queries_discard_timeout = 0;
mesh->num_queries_wait_limit = 0;
+ mesh->num_dns_error_reports = 0;
}
size_t
size_t num_queries_discard_timeout;
/** stats, number of queries removed due to wait-limit */
size_t num_queries_wait_limit;
+ /** stats, number of dns error reports generated */
+ size_t num_dns_error_reports;
/** backup of query if other operations recurse and need the
* network buffers */
LDNS_EDNS_PADDING = 12, /* RFC7830 */
LDNS_EDNS_EDE = 15, /* RFC8914 */
LDNS_EDNS_CLIENT_TAG = 16, /* draft-bellis-dnsop-edns-tags-01 */
+ LDNS_EDNS_REPORT_CHANNEL = 18, /* RFC9567 */
LDNS_EDNS_UNBOUND_CACHEDB_TESTFRAME_TEST = 65534
};
typedef enum sldns_enum_edns_option sldns_edns_option;
PR_UL_NM("num.expired", s->svr.ans_expired);
PR_UL_NM("num.recursivereplies", s->mesh_replies_sent);
#ifdef USE_DNSCRYPT
- PR_UL_NM("num.dnscrypt.crypted", s->svr.num_query_dnscrypt_crypted);
- PR_UL_NM("num.dnscrypt.cert", s->svr.num_query_dnscrypt_cert);
- PR_UL_NM("num.dnscrypt.cleartext", s->svr.num_query_dnscrypt_cleartext);
- PR_UL_NM("num.dnscrypt.malformed",
- s->svr.num_query_dnscrypt_crypted_malformed);
+ PR_UL_NM("num.dnscrypt.crypted", s->svr.num_query_dnscrypt_crypted);
+ PR_UL_NM("num.dnscrypt.cert", s->svr.num_query_dnscrypt_cert);
+ PR_UL_NM("num.dnscrypt.cleartext", s->svr.num_query_dnscrypt_cleartext);
+ PR_UL_NM("num.dnscrypt.malformed",
+ s->svr.num_query_dnscrypt_crypted_malformed);
#endif /* USE_DNSCRYPT */
+ PR_UL_NM("num.dns_error_reports", s->svr.num_dns_error_reports);
printf("%s.requestlist.avg"SQ"%g\n", nm,
(s->svr.num_queries_missed_cache+s->svr.num_queries_prefetch)?
(double)s->svr.sum_query_list_size/
--- /dev/null
+; Test DNS Error Reporting.
+
+server:
+ module-config: "validator iterator"
+ trust-anchor-signaling: no
+ target-fetch-policy: "0 0 0 0 0"
+ verbosity: 4
+ qname-minimisation: no
+ minimal-responses: no
+ rrset-roundrobin: no
+ trust-anchor: "a.domain DS 50602 8 2 FA8EE175C47325F4BD46D8A4083C3EBEB11C977D689069F2B41F1A29B22446B1"
+ ede: no # It is not needed for dns-error-reporting; only for clients to receive EDEs
+ dns-error-reporting: yes
+ do-ip6: no
+
+stub-zone:
+ name: domain
+ stub-addr: 0.0.0.0
+stub-zone:
+ name: an.agent
+ stub-addr: 0.0.0.2
+CONFIG_END
+
+SCENARIO_BEGIN Test DNS Error Reporting
+
+; domain
+RANGE_BEGIN 0 100
+ ADDRESS 0.0.0.0
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ a.domain. IN A
+ SECTION AUTHORITY
+ a.domain. IN NS ns.a.domain.
+ SECTION ADDITIONAL
+ ns.a.domain. IN A 0.0.0.1
+ HEX_EDNSDATA_BEGIN
+ 00 12 ; opt-code (Report-Channel)
+ 00 0A ; opt-len
+ 02 61 6E 05 61 67 65 6E 74 00 ; an.agent.
+ HEX_EDNSDATA_END
+ ENTRY_END
+RANGE_END
+
+; a.domain
+RANGE_BEGIN 0 9
+ ADDRESS 0.0.0.1
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ a.domain. IN DNSKEY
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ a.domain. IN A
+ SECTION ANSWER
+ a.domain. 5 IN A 0.0.0.0
+ ; No RRSIG to trigger validation error (and EDE)
+ SECTION ADDITIONAL
+ ; No Report-Channel here
+ ENTRY_END
+RANGE_END
+
+; a.domain
+RANGE_BEGIN 10 100
+ ADDRESS 0.0.0.1
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ a.domain. IN DNSKEY
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ a.domain. IN A
+ SECTION ANSWER
+ a.domain. 5 IN A 0.0.0.0
+ ; No RRSIG to trigger validator error and EDE
+ SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ 00 12 ; opt-code (Report-Channel)
+ 00 0A ; opt-len
+ 02 61 6E 05 61 67 65 6E 74 00 ; an.agent.
+ HEX_EDNSDATA_END
+ ENTRY_END
+RANGE_END
+
+; an.agent
+RANGE_BEGIN 10 20
+ ADDRESS 0.0.0.2
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ _er.1.a.domain.9._er.an.agent. IN TXT
+ SECTION ANSWER
+ _er.1.a.domain.9._er.an.agent. IN TXT "OK"
+ ENTRY_END
+RANGE_END
+
+; Query
+STEP 0 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+a.domain. IN A
+ENTRY_END
+
+; Check that validation failed (no DNS error reporting at this state;
+; 'domain' did give an error reporting agent, but the latest upstream
+; 'a.domain' did not)
+STEP 1 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+a.domain. IN A
+ENTRY_END
+
+; Wait for the a.domain query to expire (TTL 5)
+STEP 3 TIME_PASSES ELAPSE 6
+
+; Query again
+STEP 10 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+a.domain. IN A
+ENTRY_END
+
+; Check that validation failed
+; (a DNS Error Report query should have been generated)
+STEP 11 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+a.domain. IN A
+ENTRY_END
+
+; Check explicitly that the DNS Error Report query is cached.
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+_er.1.a.domain.9._er.an.agent. IN TXT
+ENTRY_END
+
+; At this range there are no configured agents to answer this.
+; If the DNS Error Report query is not answered from the cache the test will
+; fail with pending messages.
+STEP 21 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY RD QR RA NOERROR
+SECTION QUESTION
+_er.1.a.domain.9._er.an.agent. IN TXT
+SECTION ANSWER
+_er.1.a.domain.9._er.an.agent. IN TXT "OK"
+ENTRY_END
+
+; Wait for the a.domain query to expire (5 TTL).
+; The DNS Error Report query should still be cached (SOA negative).
+STEP 30 TIME_PASSES ELAPSE 6
+
+; Force a DNS Error Report query generation again.
+STEP 31 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+a.domain. IN A
+ENTRY_END
+
+; Check that validation failed
+STEP 32 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+a.domain. IN A
+ENTRY_END
+
+; The same DNS Error Report query will be generated as above.
+; No agent is configured at this range to answer the DNS Error Report query.
+; If the DNS Error Report query is not used from the cache the test will fail
+; with pending messages.
+
+SCENARIO_END
root-key-sentinel: no
trust-anchor-signaling: no
serve-expired-client-timeout: 0
+ dns-error-reporting: yes
+
+ trust-anchor: "bogusdnssec. DS 1444 8 2 5224fb17d630a2e3efdc863a05a4032c5db415b5de3f32472ee9abed42e10146"
local-zone: local.zone static
local-data: "www.local.zone A 192.0.2.1"
stub-zone:
name: "example.com."
stub-addr: "127.0.0.1@@TOPORT@"
+stub-zone:
+ name: "bogusdnssec."
+ stub-addr: "127.0.0.1@@TOPORT@"
+stub-zone:
+ name: "an.agent."
+ stub-addr: "127.0.0.1@@TOPORT@"
stub-zone:
name: "expired."
stub-addr: "127.0.0.1@@EXPIREDPORT@"
infra.cache.count=2"
+teststep "Check dns-error-reporting."
+echo "> dig www.bogusdnssec."
+dig @127.0.0.1 -p $UNBOUND_PORT www.bogusdnssec. | tee outfile
+echo "> check answer"
+if grep "SERVFAIL" outfile; then
+ echo "OK"
+else
+ end 1
+fi
+check_stats "\
+infra.cache.count=4
+key.cache.count=1
+msg.cache.count=7
+num.answer.bogus=1
+num.answer.rcode.SERVFAIL=1
+num.query.class.IN=1
+num.query.edns.present=1
+num.query.flags.AD=1
+num.query.flags.RD=1
+num.query.opcode.QUERY=1
+num.query.type.A=1
+num.query.udpout=9
+rrset.cache.count=4
+total.num.cachemiss=1
+total.num.dns_error_reports=1
+total.num.queries=1
+total.num.recursivereplies=1"
+
+
###
#
# Bring the discard-timeout, wait-limit configured Unbound up
teststep "Check discard-timeout and wait-limit"
-echo "> dig www.slow"
-dig @127.0.0.1 -p $UNBOUND_PORT +retry=2 +timeout=1 www.slow. | tee outfile
+echo "> dig www.unresponsive"
+dig @127.0.0.1 -p $UNBOUND_PORT +retry=2 +timeout=1 www.unresponsive. | tee outfile
echo "> check answer"
if grep "no servers could be reached" outfile; then
echo "OK"
0ttl 0 IN A 0.0.0.1
ENTRY_END
-$ORIGIN slow.
+
+
+$ORIGIN bogusdnssec.
ENTRY_BEGIN
MATCH opcode qtype qname
REPLY QR AA NOERROR
-ADJUST copy_id sleep=2
+ADJUST copy_id
SECTION QUESTION
-www. IN A
+@ IN DNSKEY
SECTION ANSWER
-www. 0 IN A 10.20.30.40
ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NOERROR
+ADJUST copy_id
+SECTION QUESTION
+www IN A
+SECTION ANSWER
+www 0 IN A 10.20.30.40
+; bogus signature to not trigger LAME DNSSEC and continue with validation
+www 0 IN RRSIG A 8 2 240 20250429005000 20250401005000 42393 bogusdnssec. ob6ddTJkdeOUn92cxx1NPGneV7rhOp2zKBv8FXQjJ/Wso8LJJnzRHW9p 3sTatlzi+UdRi7BOrcxwjUG38lgO+TS5vRFGAiTRmOezm6xJVNTg8lIb RJGCD5bRtRRstwt31Qt6Gda+6sAyvDebpUB/opkQpevv6xohdrhr0g8+ Q4w=
+SECTION ADDITIONAL
+; dns error reporting agent
+HEX_EDNSDATA_BEGIN
+ 00 12 ; opt-code (Report-Channel)
+ 00 0A ; opt-len
+ 02 61 6E 05 61 67 65 6E 74 00 ; an.agent.
+HEX_EDNSDATA_END
+ENTRY_END
+
+
+
+$ORIGIN an.agent.
+;just give an answer back to anything
+ENTRY_BEGIN
+MATCH opcode subdomain
+REPLY QR AA NXDOMAIN
+ADJUST copy_id copy_query
+SECTION QUESTION
+an.agent. IN ANY
+ENTRY_END
+
+
+
+$ORIGIN unresponsive.
+;; no entry for 'unresponsive.', we rely on timeouts.
control-key-file: "unbound_control.key"
control-cert-file: "unbound_control.pem"
stub-zone:
- name: "slow."
+ name: "unresponsive."
stub-addr: "127.0.0.1@@TOPORT@"
cfg->serve_expired_ttl_reset = 0;
cfg->serve_expired_reply_ttl = 30;
cfg->serve_expired_client_timeout = 1800;
- cfg->ede_serve_expired = 0;
cfg->serve_original_ttl = 0;
cfg->zonemd_permissive_mode = 0;
cfg->add_holddown = 30*24*3600;
cfg->ipset_name_v6 = NULL;
#endif
cfg->ede = 0;
+ cfg->ede_serve_expired = 0;
+ cfg->dns_error_reporting = 0;
cfg->iter_scrub_ns = 20;
cfg->iter_scrub_cname = 11;
cfg->max_global_quota = 200;
else S_NUMBER_OR_ZERO("serve-expired-client-timeout:", serve_expired_client_timeout)
else S_YNO("ede:", ede)
else S_YNO("ede-serve-expired:", ede_serve_expired)
+ else S_YNO("dns-error-reporting:", dns_error_reporting)
else S_NUMBER_OR_ZERO("iter-scrub-ns:", iter_scrub_ns)
else S_NUMBER_OR_ZERO("iter-scrub-cname:", iter_scrub_cname)
else S_NUMBER_OR_ZERO("max-global-quota:", max_global_quota)
else O_DEC(opt, "serve-expired-client-timeout", serve_expired_client_timeout)
else O_YNO(opt, "ede", ede)
else O_YNO(opt, "ede-serve-expired", ede_serve_expired)
+ else O_YNO(opt, "dns-error-reporting", dns_error_reporting)
else O_DEC(opt, "iter-scrub-ns", iter_scrub_ns)
else O_DEC(opt, "iter-scrub-cname", iter_scrub_cname)
else O_DEC(opt, "max-global-quota", max_global_quota)
/** serve expired entries only after trying to update the entries and this
* timeout (in milliseconds) is reached */
int serve_expired_client_timeout;
- /** serve EDE code 3 - Stale Answer (RFC8914) for expired entries */
- int ede_serve_expired;
/** serve original TTLs rather than decrementing ones */
int serve_original_ttl;
/** nsec3 maximum iterations per key size, string */
#endif
/** respond with Extended DNS Errors (RFC8914) */
int ede;
+ /** serve EDE code 3 - Stale Answer (RFC8914) for expired entries */
+ int ede_serve_expired;
+ /** send DNS Error Reports to upstream reporting agent (RFC9567) */
+ int dns_error_reporting;
/** limit on NS RRs in RRset for the iterator scrubber. */
size_t iter_scrub_ns;
/** limit on CNAME, DNAME RRs in answer for the iterator scrubber. */
edns-client-string-opcode{COLON} { YDVAR(1, VAR_EDNS_CLIENT_STRING_OPCODE) }
nsid{COLON} { YDVAR(1, VAR_NSID ) }
ede{COLON} { YDVAR(1, VAR_EDE ) }
+dns-error-reporting{COLON} { YDVAR(1, VAR_DNS_ERROR_REPORTING ) }
proxy-protocol-port{COLON} { YDVAR(1, VAR_PROXY_PROTOCOL_PORT) }
iter-scrub-ns{COLON} { YDVAR(1, VAR_ITER_SCRUB_NS) }
iter-scrub-cname{COLON} { YDVAR(1, VAR_ITER_SCRUB_CNAME) }
%token VAR_EDNS_CLIENT_STRING_OPCODE VAR_NSID
%token VAR_ZONEMD_PERMISSIVE_MODE VAR_ZONEMD_CHECK VAR_ZONEMD_REJECT_ABSENCE
%token VAR_RPZ_SIGNAL_NXDOMAIN_RA VAR_INTERFACE_AUTOMATIC_PORTS VAR_EDE
+%token VAR_DNS_ERROR_REPORTING
%token VAR_INTERFACE_ACTION VAR_INTERFACE_VIEW VAR_INTERFACE_TAG
%token VAR_INTERFACE_TAG_ACTION VAR_INTERFACE_TAG_DATA
%token VAR_QUIC_PORT VAR_QUIC_SIZE
server_tcp_reuse_timeout | server_tcp_auth_query_timeout |
server_quic_port | server_quic_size |
server_interface_automatic_ports | server_ede |
+ server_dns_error_reporting |
server_proxy_protocol_port | server_statistics_inhibit_zero |
server_harden_unknown_additional | server_disable_edns_do |
server_log_destaddr | server_cookie_secret_file |
free($2);
}
;
+server_dns_error_reporting: VAR_DNS_ERROR_REPORTING STRING_ARG
+ {
+ OUTYY(("P(server_dns_error_reporting:%s)\n", $2));
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+ yyerror("expected yes or no.");
+ else cfg_parser->cfg->dns_error_reporting = (strcmp($2, "yes")==0);
+ free($2);
+ }
+ ;
server_proxy_protocol_port: VAR_PROXY_PROTOCOL_PORT STRING_ARG
{
OUTYY(("P(server_proxy_protocol_port:%s)\n", $2));