mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-22 01:59:26 +00:00
Add send-report-channel option
This commit adds support for the EDNS Report-Channel option, which is returned in authoritative responses when EDNS is in use. "send-report-channel" sets the Agent-Domain value that will be included in EDNS Report-Channel options. This is configurable at the options/view level; the value is a DNS name. Setting the Agent-Domain to the root zone (".") disables the option. When this value has been set, incoming queries matchng the form _er.<qtype>.<qname>.<extended-error-code>._er.<agent-domain>/TXT will be logged to the dns-reporting-agent channel at INFO level. (Note: error reporting queries will only be accepted if sent via TCP or with a good server cookie. If neither is present, named returns BADCOOKIE to complete the DNS COOKIE handshake, or TC=1 to switch the client to TCP.)
This commit is contained in:
parent
80f611afe6
commit
ac1c60d87e
@ -4271,6 +4271,22 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
|
||||
}
|
||||
}
|
||||
|
||||
obj = NULL;
|
||||
result = named_config_get(maps, "send-report-channel", &obj);
|
||||
if (view->rad != NULL) {
|
||||
dns_name_free(view->rad, view->mctx);
|
||||
isc_mem_put(view->mctx, view->rad, sizeof(*view->rad));
|
||||
}
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
str = cfg_obj_asstring(obj);
|
||||
if (strcmp(str, ".") != 0 && strcmp(str, "") != 0) {
|
||||
view->rad = isc_mem_get(mctx, sizeof(*view->rad));
|
||||
dns_name_init(view->rad, NULL);
|
||||
CHECK(dns_name_fromstring(view->rad, str, dns_rootname,
|
||||
0, mctx));
|
||||
}
|
||||
}
|
||||
|
||||
obj = NULL;
|
||||
result = named_config_get(maps, "dnssec-accept-expired", &obj);
|
||||
INSIST(result == ISC_R_SUCCESS);
|
||||
|
@ -11,10 +11,11 @@
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
rm -f */named.conf
|
||||
rm -f */named.memstats
|
||||
rm -f */named.run
|
||||
rm -f */named.conf
|
||||
rm -f */named.run.prev
|
||||
rm -f dig.out.test*
|
||||
rm -f ns*/managed-keys.bind* ns*/*mkeys*
|
||||
rm -f ns2/example.com.bk
|
||||
rm -f ns2/example.net.bk
|
||||
rm -f ns*/managed-keys.bind* ns*/*mkeys*
|
||||
|
@ -22,6 +22,7 @@ options {
|
||||
recursion no;
|
||||
notify yes;
|
||||
dnssec-validation no;
|
||||
send-report-channel "rad.example.net";
|
||||
};
|
||||
|
||||
view main in {
|
||||
|
@ -186,5 +186,23 @@ lines=$(wc -l <dig.out.test$n)
|
||||
[ $ret -eq 0 ] || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "check that a Report-Channel EDNS option is added to responses ($n)"
|
||||
ret=0
|
||||
$DIG $DIGOPTS @10.53.0.1 example.net >dig.out.test$n
|
||||
grep "; Report-Channel: rad.example.net" dig.out.test$n >/dev/null || ret=1
|
||||
[ $ret -eq 0 ] || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "check that error report queries are logged and no Report-Channel option is present in the response ($n)"
|
||||
ret=0
|
||||
nextpart ns1/named.run >/dev/null
|
||||
$DIG $DIGOPTS @10.53.0.1 _er.0.example.1._er.rad.example.net TXT >dig.out.test$n
|
||||
nextpart ns1/named.run | grep "dns-reporting-agent '_er.0.example.1._er.rad.example.net/IN'" >/dev/null || ret=1
|
||||
grep "; Report-Channel: rad.example.net" dig.out.test$n >/dev/null && ret=1
|
||||
[ $ret -eq 0 ] || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "exit status: $status"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
|
17
bin/tests/system/checkconf/bad-rad.conf
Normal file
17
bin/tests/system/checkconf/bad-rad.conf
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* 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 https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
options {
|
||||
/* invalid domain name */
|
||||
send-report-channel example..com;
|
||||
};
|
16
bin/tests/system/checkconf/good-rad-view.conf
Normal file
16
bin/tests/system/checkconf/good-rad-view.conf
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* 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 https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
view view {
|
||||
send-report-channel example.com;
|
||||
};
|
21
bin/tests/system/checkconf/good-rad.conf
Normal file
21
bin/tests/system/checkconf/good-rad.conf
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* 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 https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
options {
|
||||
send-report-channel example.com;
|
||||
};
|
||||
|
||||
zone example.com {
|
||||
type primary;
|
||||
file "example.db";
|
||||
};
|
@ -772,5 +772,16 @@ if [ $ret != 0 ]; then
|
||||
fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "check that 'send-report-channel' warns if no matching zone exists ($n)"
|
||||
ret=0
|
||||
$CHECKCONF -z warn-rad.conf >checkconf.out$n 2>&1 || ret=1
|
||||
grep -F "send-report-channel 'example.com' is not a primary or secondary zone" checkconf.out$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then
|
||||
echo_i "failed"
|
||||
ret=1
|
||||
fi
|
||||
status=$((status + ret))
|
||||
|
||||
echo_i "exit status: $status"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
|
17
bin/tests/system/checkconf/warn-rad.conf
Normal file
17
bin/tests/system/checkconf/warn-rad.conf
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* 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 https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
options {
|
||||
/* nonexistent zone used as agent-domain */
|
||||
send-report-channel example.com;
|
||||
};
|
@ -388,6 +388,8 @@ in the DNS.* February 2014.
|
||||
:rfc:`8749` - W. Mekking and D. Mahoney. *Moving DNSSEC Lookaside Validation
|
||||
(DLV) to Historic Status.* March 2020.
|
||||
|
||||
:rfc:`9567` - R. Arends and M. Larson. *DNS Error Reporting*
|
||||
|
||||
Notes
|
||||
~~~~~
|
||||
|
||||
|
@ -43,6 +43,9 @@
|
||||
``general``
|
||||
A catch-all for many things that still are not classified into categories.
|
||||
|
||||
``dns-reporting-agent``
|
||||
Logs reports from clients that the there is an error in our responses.
|
||||
|
||||
``lame-servers``
|
||||
Misconfigurations in remote servers, discovered by BIND 9 when trying to query those servers during resolution.
|
||||
|
||||
|
@ -1680,6 +1680,41 @@ default is used.
|
||||
If all supported digest types are disabled, the zones covered by
|
||||
:any:`disable-ds-digests` are treated as insecure.
|
||||
|
||||
.. namedconf:statement:: send-report-channel
|
||||
:tags: query
|
||||
:short: Sets the Agent Domain value for the EDNS Report-Channel option
|
||||
|
||||
The EDNS Report-Channel option can be added to responses by an
|
||||
authoritative server to inform clients of a domain name to which
|
||||
operational and protocol errors may be reported. This can help
|
||||
operators find out about configuration errors that are causing
|
||||
problems with resolution or validation elsewhere (for example,
|
||||
expired DNSSEC signatures).
|
||||
|
||||
When :any:`send-report-channel` is set in :namedconf:ref:`options`, or
|
||||
:namedconf:ref:`view` :iscman:`named` adds a Report-Channel option to
|
||||
authoritative responses, using the specified domain name as the
|
||||
Agent-Domain. :iscman:`named` also logs any TXT queries received for
|
||||
names matching the prescribed error-reporting format
|
||||
(_er.<type>.<name>.<extended-rcode>._er.<agent-domain>) to the
|
||||
``dns-reporting-agent`` logging category at level ``info``.
|
||||
|
||||
There should be a zone delegated to respond to these queries with TXT
|
||||
records, to avoid unnecessarily query repetition. For example:
|
||||
|
||||
::
|
||||
|
||||
e.g.
|
||||
$ORIGIN <agent-domain>
|
||||
@ 600 SOA <namserver1> <contact-email> 0 0 0 0 600
|
||||
@ 600 NS <nameserver1>
|
||||
@ 600 NS <nameserver2>
|
||||
*._er 600 TXT ""
|
||||
|
||||
If :any:`send-report-channel` is not set, or is set to ``.``, then
|
||||
the EDNS option is not added to responses, and error-report queries are
|
||||
not logged.
|
||||
|
||||
.. namedconf:statement:: dnssec-must-be-secure
|
||||
:tags: deprecated
|
||||
:short: Defines hierarchies that must or may not be secure (signed and validated).
|
||||
|
@ -266,6 +266,7 @@ options {
|
||||
rrset-order { [ class <string> ] [ type <string> ] [ name <quoted_string> ] <string> <string>; ... };
|
||||
secroots-file <quoted_string>;
|
||||
send-cookie <boolean>;
|
||||
send-report-channel <string>;
|
||||
serial-query-rate <integer>;
|
||||
serial-update-method ( date | increment | unixtime );
|
||||
server-id ( <quoted_string> | none | hostname );
|
||||
@ -545,6 +546,7 @@ view <string> [ <class> ] {
|
||||
root-key-sentinel <boolean>;
|
||||
rrset-order { [ class <string> ] [ type <string> ] [ name <quoted_string> ] <string> <string>; ... };
|
||||
send-cookie <boolean>;
|
||||
send-report-channel <string>;
|
||||
serial-update-method ( date | increment | unixtime );
|
||||
server <netprefix> {
|
||||
bogus <boolean>;
|
||||
|
@ -102,18 +102,20 @@
|
||||
#define DNS_MESSAGEEXTFLAG_DO 0x8000U
|
||||
|
||||
/*%< EDNS0 extended OPT codes */
|
||||
#define DNS_OPT_LLQ 1 /*%< LLQ opt code */
|
||||
#define DNS_OPT_UL 2 /*%< UL opt code */
|
||||
#define DNS_OPT_NSID 3 /*%< NSID opt code */
|
||||
#define DNS_OPT_CLIENT_SUBNET 8 /*%< client subnet opt code */
|
||||
#define DNS_OPT_EXPIRE 9 /*%< EXPIRE opt code */
|
||||
#define DNS_OPT_COOKIE 10 /*%< COOKIE opt code */
|
||||
#define DNS_OPT_TCP_KEEPALIVE 11 /*%< TCP keepalive opt code */
|
||||
#define DNS_OPT_PAD 12 /*%< PAD opt code */
|
||||
#define DNS_OPT_KEY_TAG 14 /*%< Key tag opt code */
|
||||
#define DNS_OPT_EDE 15 /*%< Extended DNS Error opt code */
|
||||
#define DNS_OPT_CLIENT_TAG 16 /*%< Client tag opt code */
|
||||
#define DNS_OPT_SERVER_TAG 17 /*%< Server tag opt code */
|
||||
|
||||
#define DNS_OPT_LLQ 1 /*%< LLQ opt code */
|
||||
#define DNS_OPT_UL 2 /*%< UL opt code */
|
||||
#define DNS_OPT_NSID 3 /*%< NSID opt code */
|
||||
#define DNS_OPT_CLIENT_SUBNET 8 /*%< client subnet opt code */
|
||||
#define DNS_OPT_EXPIRE 9 /*%< EXPIRE opt code */
|
||||
#define DNS_OPT_COOKIE 10 /*%< COOKIE opt code */
|
||||
#define DNS_OPT_TCP_KEEPALIVE 11 /*%< TCP keepalive opt code */
|
||||
#define DNS_OPT_PAD 12 /*%< PAD opt code */
|
||||
#define DNS_OPT_KEY_TAG 14 /*%< Key tag opt code */
|
||||
#define DNS_OPT_EDE 15 /*%< Extended DNS Error opt code */
|
||||
#define DNS_OPT_CLIENT_TAG 16 /*%< Client tag opt code */
|
||||
#define DNS_OPT_SERVER_TAG 17 /*%< Server tag opt code */
|
||||
#define DNS_OPT_REPORT_CHANNEL 18 /*%< DNS Reporting Channel */
|
||||
|
||||
/*%< Experimental options [65001...65534] as per RFC6891 */
|
||||
|
||||
@ -122,7 +124,7 @@
|
||||
* options we know about. Extended DNS Errors may occur multiple times, but we
|
||||
* will set only one per message (for now).
|
||||
*/
|
||||
#define DNS_EDNSOPTIONS 8
|
||||
#define DNS_EDNSOPTIONS 9
|
||||
|
||||
/*%< EDNS0 extended DNS errors */
|
||||
#define DNS_EDE_OTHER 0 /*%< Other Error */
|
||||
|
@ -1350,6 +1350,19 @@ dns_name_size(const dns_name_t *name);
|
||||
/*%<
|
||||
* Return the amount of dynamically allocated memory associated with
|
||||
* 'name' (which is 0 if 'name' is not dynamic).
|
||||
*/
|
||||
|
||||
bool
|
||||
dns_name_israd(const dns_name_t *name, const dns_name_t *rad);
|
||||
/*%<
|
||||
* Determine whether 'name' matches the prescribed format of a
|
||||
* DNS error-reporting name:
|
||||
*
|
||||
* _er.<TYPE>.<QNAME>.<EDE>._er.<AGENT-DOMAIN>.
|
||||
*
|
||||
* AGENT-DOMAIN is specified by the 'rad' parameter.
|
||||
* EDE is a numeric value representing an extended DNS error code.
|
||||
* TYPE and EDE are not currently checked.
|
||||
*
|
||||
* Requires:
|
||||
* \li 'name' to be valid.
|
||||
|
@ -186,6 +186,7 @@ struct dns_view {
|
||||
uint32_t maxrrperset;
|
||||
uint32_t maxtypepername;
|
||||
uint8_t max_restarts;
|
||||
dns_name_t *rad; /* reporting agent domain */
|
||||
|
||||
/*
|
||||
* Configurable data for server use only,
|
||||
|
@ -3606,6 +3606,26 @@ cleanup:
|
||||
return (result);
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
render_reportchan(isc_buffer_t *optbuf, isc_buffer_t *target) {
|
||||
dns_decompress_t dctx = DNS_DECOMPRESS_NEVER;
|
||||
dns_fixedname_t fixed;
|
||||
dns_name_t *name = dns_fixedname_initname(&fixed);
|
||||
char namebuf[DNS_NAME_FORMATSIZE];
|
||||
isc_result_t result;
|
||||
|
||||
result = dns_name_fromwire(name, optbuf, dctx, NULL);
|
||||
if (result == ISC_R_SUCCESS && isc_buffer_activelength(optbuf) == 0) {
|
||||
dns_name_format(name, namebuf, sizeof(namebuf));
|
||||
ADD_STRING(target, " ");
|
||||
ADD_STRING(target, namebuf);
|
||||
return (result);
|
||||
}
|
||||
result = ISC_R_FAILURE;
|
||||
cleanup:
|
||||
return (result);
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section,
|
||||
const dns_master_style_t *style,
|
||||
@ -3862,6 +3882,19 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section,
|
||||
ADD_STRING(target, buf);
|
||||
continue;
|
||||
}
|
||||
} else if (optcode == DNS_OPT_REPORT_CHANNEL) {
|
||||
INDENT(style);
|
||||
ADD_STRING(target, "Report-Channel:");
|
||||
if (optlen > 0U) {
|
||||
isc_buffer_t sb = optbuf;
|
||||
isc_buffer_setactive(&optbuf, optlen);
|
||||
result = render_reportchan(&optbuf,
|
||||
target);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
optbuf = sb;
|
||||
}
|
||||
} else {
|
||||
INDENT(style);
|
||||
ADD_STRING(target, "OPT=");
|
||||
@ -4249,6 +4282,19 @@ dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section,
|
||||
ADD_STRING(target, buf);
|
||||
continue;
|
||||
}
|
||||
} else if (optcode == DNS_OPT_REPORT_CHANNEL) {
|
||||
ADD_STRING(target, "; Report-Channel:");
|
||||
if (optlen > 0U) {
|
||||
isc_buffer_t sb = optbuf;
|
||||
isc_buffer_setactive(&optbuf, optlen);
|
||||
result = render_reportchan(&optbuf,
|
||||
target);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
ADD_STRING(target, "\n");
|
||||
continue;
|
||||
}
|
||||
optbuf = sb;
|
||||
}
|
||||
} else {
|
||||
ADD_STRING(target, "; OPT=");
|
||||
snprintf(buf, sizeof(buf), "%u:", optcode);
|
||||
|
@ -2281,3 +2281,72 @@ dns_name_isdnssvcb(const dns_name_t *name) {
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
dns_name_israd(const dns_name_t *name, const dns_name_t *rad) {
|
||||
dns_name_t suffix;
|
||||
dns_offsets_t offsets;
|
||||
char labelbuf[64];
|
||||
unsigned long v, last = ULONG_MAX;
|
||||
char *end, *l;
|
||||
|
||||
REQUIRE(DNS_NAME_VALID(name));
|
||||
REQUIRE(DNS_NAME_VALID(rad));
|
||||
|
||||
if (name->labels < rad->labels + 4U || name->length < 4U) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (name->ndata[0] != 3 || name->ndata[1] != '_' ||
|
||||
tolower(name->ndata[2]) != 'e' || tolower(name->ndata[3]) != 'r')
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
dns_name_init(&suffix, offsets);
|
||||
dns_name_split(name, rad->labels + 1, NULL, &suffix);
|
||||
|
||||
if (suffix.ndata[0] != 3 || suffix.ndata[1] != '_' ||
|
||||
tolower(suffix.ndata[2]) != 'e' || tolower(suffix.ndata[3]) != 'r')
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* type list */
|
||||
dns_name_split(name, name->labels - 1, NULL, &suffix);
|
||||
INSIST(*suffix.ndata < sizeof(labelbuf));
|
||||
memmove(labelbuf, suffix.ndata + 1, *suffix.ndata);
|
||||
labelbuf[*suffix.ndata] = 0;
|
||||
if (strlen(labelbuf) != *suffix.ndata) {
|
||||
return (false);
|
||||
}
|
||||
l = labelbuf;
|
||||
do {
|
||||
v = strtoul(l, &end, 10);
|
||||
if (v > 0xffff || (*end != 0 && *end != '-') || end == l) {
|
||||
return (false);
|
||||
}
|
||||
if (last != ULONG_MAX && v <= last) {
|
||||
return (false);
|
||||
}
|
||||
last = v;
|
||||
if (*end == '-') {
|
||||
l = end + 1;
|
||||
}
|
||||
} while (*end != 0);
|
||||
|
||||
/* extended error code */
|
||||
dns_name_split(name, rad->labels + 2, NULL, &suffix);
|
||||
INSIST(*suffix.ndata < sizeof(labelbuf));
|
||||
memmove(labelbuf, suffix.ndata + 1, *suffix.ndata);
|
||||
labelbuf[*suffix.ndata] = 0;
|
||||
if (strlen(labelbuf) != *suffix.ndata) {
|
||||
return (false);
|
||||
}
|
||||
v = strtoul(labelbuf, &end, 10);
|
||||
if (v > 0xfff || *end != 0) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (dns_name_issubdomain(name, rad));
|
||||
}
|
||||
|
@ -93,17 +93,22 @@ totext_opt(ARGS_TOTEXT) {
|
||||
|
||||
static isc_result_t
|
||||
fromwire_opt(ARGS_FROMWIRE) {
|
||||
dns_fixedname_t fixed;
|
||||
dns_name_t *name;
|
||||
isc_buffer_t b;
|
||||
isc_region_t sregion;
|
||||
isc_region_t tregion;
|
||||
uint16_t opt;
|
||||
isc_result_t result;
|
||||
uint16_t length;
|
||||
uint16_t opt;
|
||||
unsigned int total;
|
||||
|
||||
REQUIRE(type == dns_rdatatype_opt);
|
||||
|
||||
UNUSED(type);
|
||||
UNUSED(rdclass);
|
||||
UNUSED(dctx);
|
||||
|
||||
dctx = dns_decompress_setpermitted(dctx, false);
|
||||
|
||||
isc_buffer_activeregion(source, &sregion);
|
||||
if (sregion.length == 0) {
|
||||
@ -245,6 +250,22 @@ fromwire_opt(ARGS_FROMWIRE) {
|
||||
}
|
||||
isc_region_consume(&sregion, length);
|
||||
break;
|
||||
case DNS_OPT_REPORT_CHANNEL:
|
||||
/* A domain name in wire format. RFC 9567 */
|
||||
if (length == 0 || length > DNS_NAME_MAXWIRE) {
|
||||
return (DNS_R_OPTERR);
|
||||
}
|
||||
isc_buffer_init(&b, sregion.base, length);
|
||||
isc_buffer_add(&b, length);
|
||||
name = dns_fixedname_initname(&fixed);
|
||||
result = dns_name_fromwire(name, &b, dctx, NULL);
|
||||
if (result != ISC_R_SUCCESS || name->length != length ||
|
||||
!dns_name_isabsolute(name))
|
||||
{
|
||||
return (DNS_R_OPTERR);
|
||||
}
|
||||
isc_region_consume(&sregion, length);
|
||||
break;
|
||||
default:
|
||||
isc_region_consume(&sregion, length);
|
||||
break;
|
||||
|
@ -376,6 +376,10 @@ destroy(dns_view_t *view) {
|
||||
dns_dns64_unlink(&view->dns64, dns64);
|
||||
dns_dns64_destroy(&dns64);
|
||||
}
|
||||
if (view->rad != NULL) {
|
||||
dns_name_free(view->rad, view->mctx);
|
||||
isc_mem_put(view->mctx, view->rad, sizeof(*view->rad));
|
||||
}
|
||||
if (view->managed_keys != NULL) {
|
||||
dns_zone_detach(&view->managed_keys);
|
||||
}
|
||||
|
@ -149,6 +149,7 @@ enum isc_logcategory {
|
||||
NS_LOGCATEGORY_TAT,
|
||||
NS_LOGCATEGORY_SERVE_STALE,
|
||||
NS_LOGCATEGORY_RESPONSES,
|
||||
NS_LOGCATEGORY_DRA,
|
||||
/* cfg categories */
|
||||
CFG_LOGCATEGORY_CONFIG,
|
||||
/* named categories */
|
||||
|
@ -197,6 +197,7 @@ static const char *categories_description[] = {
|
||||
[NS_LOGCATEGORY_UPDATE_SECURITY] = "update-security",
|
||||
[NS_LOGCATEGORY_QUERY_ERRORS] = "query-errors",
|
||||
[NS_LOGCATEGORY_TAT] = "trust-anchor-telemetry",
|
||||
[NS_LOGCATEGORY_DRA] = "dns-reporting-agent",
|
||||
[NS_LOGCATEGORY_SERVE_STALE] = "serve-stale",
|
||||
[NS_LOGCATEGORY_RESPONSES] = "responses",
|
||||
/* cfg categories */
|
||||
|
@ -1617,6 +1617,23 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check send-report-channel.
|
||||
*/
|
||||
obj = NULL;
|
||||
(void)cfg_map_get(options, "send-report-channel", &obj);
|
||||
if (obj != NULL) {
|
||||
str = cfg_obj_asstring(obj);
|
||||
tresult = check_name(str);
|
||||
if (tresult != ISC_R_SUCCESS) {
|
||||
cfg_obj_log(obj, ISC_LOG_ERROR,
|
||||
"'%s' is not a valid name", str);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
result = tresult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check dnssec-must-be-secure.
|
||||
*/
|
||||
@ -5053,28 +5070,73 @@ cleanup:
|
||||
|
||||
typedef enum { special_zonetype_rpz, special_zonetype_catz } special_zonetype_t;
|
||||
|
||||
static bool
|
||||
iszone(const cfg_obj_t *nameobj, const char *what, const char *forview,
|
||||
const char *viewname, int level, isc_symtab_t *symtab) {
|
||||
char namebuf[DNS_NAME_FORMATSIZE];
|
||||
const cfg_obj_t *obj = NULL;
|
||||
const char *zonename = cfg_obj_asstring(nameobj);
|
||||
const char *zonetype = "";
|
||||
dns_fixedname_t fixed;
|
||||
dns_name_t *name = dns_fixedname_initname(&fixed);
|
||||
isc_result_t result;
|
||||
isc_symvalue_t value;
|
||||
|
||||
if (viewname == NULL) {
|
||||
viewname = "";
|
||||
forview = "";
|
||||
}
|
||||
|
||||
result = dns_name_fromstring(name, zonename, dns_rootname, 0, NULL);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
cfg_obj_log(nameobj, ISC_LOG_ERROR, "bad domain name '%s'",
|
||||
zonename);
|
||||
return (false);
|
||||
}
|
||||
|
||||
dns_name_format(name, namebuf, sizeof(namebuf));
|
||||
result = isc_symtab_lookup(symtab, namebuf, 3, &value);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
const cfg_obj_t *zoneobj = value.as_cpointer;
|
||||
if (zoneobj != NULL && cfg_obj_istuple(zoneobj)) {
|
||||
zoneobj = cfg_tuple_get(zoneobj, "options");
|
||||
}
|
||||
if (zoneobj != NULL && cfg_obj_ismap(zoneobj)) {
|
||||
(void)cfg_map_get(zoneobj, "type", &obj);
|
||||
}
|
||||
if (obj != NULL) {
|
||||
zonetype = cfg_obj_asstring(obj);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcasecmp(zonetype, "primary") != 0 &&
|
||||
strcasecmp(zonetype, "master") != 0 &&
|
||||
strcasecmp(zonetype, "secondary") != 0 &&
|
||||
strcasecmp(zonetype, "slave") != 0)
|
||||
{
|
||||
cfg_obj_log(nameobj, level,
|
||||
"%s '%s'%s%s is not a primary or secondary zone",
|
||||
what, zonename, forview, viewname);
|
||||
return (false);
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
check_rpz_catz(const char *rpz_catz, const cfg_obj_t *rpz_obj,
|
||||
const char *viewname, isc_symtab_t *symtab,
|
||||
special_zonetype_t specialzonetype) {
|
||||
const cfg_listelt_t *element;
|
||||
const cfg_obj_t *obj, *nameobj, *zoneobj;
|
||||
const char *zonename, *zonetype;
|
||||
const cfg_obj_t *obj, *nameobj;
|
||||
const char *forview = " for view ";
|
||||
isc_symvalue_t value;
|
||||
isc_result_t result, tresult;
|
||||
dns_fixedname_t fixed;
|
||||
dns_name_t *name;
|
||||
char namebuf[DNS_NAME_FORMATSIZE];
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
unsigned int num_zones = 0;
|
||||
|
||||
if (viewname == NULL) {
|
||||
viewname = "";
|
||||
forview = "";
|
||||
}
|
||||
result = ISC_R_SUCCESS;
|
||||
|
||||
name = dns_fixedname_initname(&fixed);
|
||||
obj = cfg_tuple_get(rpz_obj, "zone list");
|
||||
|
||||
for (element = cfg_list_first(obj); element != NULL;
|
||||
@ -5082,56 +5144,21 @@ check_rpz_catz(const char *rpz_catz, const cfg_obj_t *rpz_obj,
|
||||
{
|
||||
obj = cfg_listelt_value(element);
|
||||
nameobj = cfg_tuple_get(obj, "zone name");
|
||||
zonename = cfg_obj_asstring(nameobj);
|
||||
zonetype = "";
|
||||
|
||||
if (specialzonetype == special_zonetype_rpz) {
|
||||
if (++num_zones > 64) {
|
||||
cfg_obj_log(nameobj, ISC_LOG_ERROR,
|
||||
"more than 64 response policy "
|
||||
"zones in view '%s'",
|
||||
viewname);
|
||||
"zones%s'%s'",
|
||||
forview, viewname);
|
||||
return (ISC_R_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
tresult = dns_name_fromstring(name, zonename, dns_rootname, 0,
|
||||
NULL);
|
||||
if (tresult != ISC_R_SUCCESS) {
|
||||
cfg_obj_log(nameobj, ISC_LOG_ERROR,
|
||||
"bad domain name '%s'", zonename);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
result = tresult;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
dns_name_format(name, namebuf, sizeof(namebuf));
|
||||
tresult = isc_symtab_lookup(symtab, namebuf, 3, &value);
|
||||
if (tresult == ISC_R_SUCCESS) {
|
||||
obj = NULL;
|
||||
zoneobj = value.as_cpointer;
|
||||
if (zoneobj != NULL && cfg_obj_istuple(zoneobj)) {
|
||||
zoneobj = cfg_tuple_get(zoneobj, "options");
|
||||
}
|
||||
if (zoneobj != NULL && cfg_obj_ismap(zoneobj)) {
|
||||
(void)cfg_map_get(zoneobj, "type", &obj);
|
||||
}
|
||||
if (obj != NULL) {
|
||||
zonetype = cfg_obj_asstring(obj);
|
||||
}
|
||||
}
|
||||
if (strcasecmp(zonetype, "primary") != 0 &&
|
||||
strcasecmp(zonetype, "master") != 0 &&
|
||||
strcasecmp(zonetype, "secondary") != 0 &&
|
||||
strcasecmp(zonetype, "slave") != 0)
|
||||
if (!iszone(nameobj, rpz_catz, forview, viewname, ISC_LOG_ERROR,
|
||||
symtab))
|
||||
{
|
||||
cfg_obj_log(nameobj, ISC_LOG_ERROR,
|
||||
"%s '%s'%s%s is not a primary or secondary "
|
||||
"zone",
|
||||
rpz_catz, zonename, forview, viewname);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
result = ISC_R_FAILURE;
|
||||
}
|
||||
result = ISC_R_FAILURE;
|
||||
}
|
||||
}
|
||||
return (result);
|
||||
@ -5411,7 +5438,8 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions,
|
||||
|
||||
/*
|
||||
* Check that the response-policy and catalog-zones options
|
||||
* refer to zones that exist.
|
||||
* refer to zones that exist. Also warn if send-report-channel
|
||||
* is not a zone.
|
||||
*/
|
||||
if (opts != NULL) {
|
||||
obj = NULL;
|
||||
@ -5433,6 +5461,14 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions,
|
||||
{
|
||||
result = ISC_R_FAILURE;
|
||||
}
|
||||
|
||||
obj = NULL;
|
||||
if ((cfg_map_get(opts, "send-report-channel", &obj) ==
|
||||
ISC_R_SUCCESS))
|
||||
{
|
||||
(void)iszone(obj, "send-report-channel", " for view ",
|
||||
viewname, ISC_LOG_WARNING, symtab);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2132,6 +2132,7 @@ static cfg_clausedef_t view_clauses[] = {
|
||||
{ "queryport-pool-updateinterval", NULL, CFG_CLAUSEFLAG_ANCIENT },
|
||||
{ "rate-limit", &cfg_type_rrl, 0 },
|
||||
{ "recursion", &cfg_type_boolean, 0 },
|
||||
{ "send-report-channel", &cfg_type_astring, 0 },
|
||||
{ "request-nsid", &cfg_type_boolean, 0 },
|
||||
{ "request-sit", NULL, CFG_CLAUSEFLAG_ANCIENT },
|
||||
{ "require-server-cookie", &cfg_type_boolean, 0 },
|
||||
|
@ -97,13 +97,11 @@
|
||||
#define COOKIE_SIZE 24U /* 8 + 4 + 4 + 8 */
|
||||
#define ECS_SIZE 20U /* 2 + 1 + 1 + [0..16] */
|
||||
|
||||
#define TCPBUFFERS_FILLCOUNT 1U
|
||||
#define TCPBUFFERS_FREEMAX 8U
|
||||
|
||||
#define WANTNSID(x) (((x)->attributes & NS_CLIENTATTR_WANTNSID) != 0)
|
||||
#define WANTEXPIRE(x) (((x)->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0)
|
||||
#define WANTPAD(x) (((x)->attributes & NS_CLIENTATTR_WANTPAD) != 0)
|
||||
#define USEKEEPALIVE(x) (((x)->attributes & NS_CLIENTATTR_USEKEEPALIVE) != 0)
|
||||
#define WANTEXPIRE(x) (((x)->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0)
|
||||
#define WANTNSID(x) (((x)->attributes & NS_CLIENTATTR_WANTNSID) != 0)
|
||||
#define WANTPAD(x) (((x)->attributes & NS_CLIENTATTR_WANTPAD) != 0)
|
||||
#define WANTRC(x) (((x)->attributes & NS_CLIENTATTR_WANTRC) != 0)
|
||||
|
||||
#define MANAGER_MAGIC ISC_MAGIC('N', 'S', 'C', 'm')
|
||||
#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, MANAGER_MAGIC)
|
||||
@ -1238,6 +1236,16 @@ no_nsid:
|
||||
count++;
|
||||
}
|
||||
|
||||
if (WANTRC(client) && view != NULL && view->rad != NULL &&
|
||||
!dns_name_equal(view->rad, dns_rootname))
|
||||
{
|
||||
INSIST(count < DNS_EDNSOPTIONS);
|
||||
ednsopts[count].code = DNS_OPT_REPORT_CHANNEL;
|
||||
ednsopts[count].length = view->rad->length;
|
||||
ednsopts[count].value = view->rad->ndata;
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Padding must be added last */
|
||||
if ((view != NULL) && (view->padding > 0) && WANTPAD(client) &&
|
||||
(TCP_CLIENT(client) ||
|
||||
|
@ -247,7 +247,7 @@ struct ns_client {
|
||||
#define NS_CLIENTATTR_WANTNSID 0x00020 /*%< include nameserver ID */
|
||||
#define NS_CLIENTATTR_BADCOOKIE \
|
||||
0x00040 /*%< Presented cookie is bad/out-of-date */
|
||||
/* Obsolete: NS_CLIENTATTR_FILTER_AAAA_RC 0x00080 */
|
||||
#define NS_CLIENTATTR_WANTRC 0x00080 /*%< include Report-Channel */
|
||||
#define NS_CLIENTATTR_WANTAD 0x00100 /*%< want AD in response if possible */
|
||||
#define NS_CLIENTATTR_WANTCOOKIE 0x00200 /*%< return a COOKIE */
|
||||
#define NS_CLIENTATTR_HAVECOOKIE 0x00400 /*%< has a valid COOKIE */
|
||||
@ -257,8 +257,8 @@ struct ns_client {
|
||||
#define NS_CLIENTATTR_HAVEECS 0x04000 /*%< received an ECS option */
|
||||
#define NS_CLIENTATTR_WANTPAD 0x08000 /*%< pad reply */
|
||||
#define NS_CLIENTATTR_USEKEEPALIVE 0x10000 /*%< use TCP keepalive */
|
||||
|
||||
#define NS_CLIENTATTR_NOSETFC 0x20000 /*%< don't set servfail cache */
|
||||
#define NS_CLIENTATTR_NOSETFC 0x20000 /*%< don't set servfail cache */
|
||||
#define NS_CLIENTATTR_NEEDTCP 0x40000 /*%< send TC=1 */
|
||||
|
||||
/*
|
||||
* Flag to use with the SERVFAIL cache to indicate
|
||||
|
@ -107,6 +107,8 @@
|
||||
(((c)->query.attributes & NS_QUERYATTR_WANTRECURSION) != 0)
|
||||
/*% Is TCP? */
|
||||
#define TCP(c) (((c)->attributes & NS_CLIENTATTR_TCP) != 0)
|
||||
/*% This query needs to have been sent over TCP. Return TC=1. */
|
||||
#define NEEDTCP(c) (((c)->attributes & NS_CLIENTATTR_NEEDTCP) != 0)
|
||||
|
||||
/*% Want DNSSEC? */
|
||||
#define WANTDNSSEC(c) (((c)->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0)
|
||||
@ -5363,6 +5365,17 @@ ns__query_start(query_ctx_t *qctx) {
|
||||
qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AA;
|
||||
qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AD;
|
||||
qctx->client->message->rcode = dns_rcode_badcookie;
|
||||
qctx->client->attributes &= ~NS_CLIENTATTR_WANTRC;
|
||||
return (ns_query_done(qctx));
|
||||
}
|
||||
|
||||
/*
|
||||
* Respond with TC=1 if we need TCP for this request.
|
||||
*/
|
||||
if (!TCP(qctx->client) && NEEDTCP(qctx->client)) {
|
||||
qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AA;
|
||||
qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AD;
|
||||
qctx->client->message->flags |= DNS_MESSAGEFLAG_TC;
|
||||
return (ns_query_done(qctx));
|
||||
}
|
||||
|
||||
@ -6891,6 +6904,8 @@ query_checkrrl(query_ctx_t *qctx, isc_result_t result) {
|
||||
~DNS_MESSAGEFLAG_AD;
|
||||
qctx->client->message->rcode =
|
||||
dns_rcode_badcookie;
|
||||
qctx->client->attributes &=
|
||||
~NS_CLIENTATTR_WANTRC;
|
||||
} else {
|
||||
qctx->client->message->flags |=
|
||||
DNS_MESSAGEFLAG_TC;
|
||||
@ -11323,6 +11338,7 @@ ns_query_done(query_ctx_t *qctx) {
|
||||
*/
|
||||
if (qctx->client->query.restarts == 0 && !qctx->authoritative) {
|
||||
qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AA;
|
||||
qctx->client->attributes &= ~NS_CLIENTATTR_WANTRC;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -11459,6 +11475,54 @@ cleanup:
|
||||
return (result);
|
||||
}
|
||||
|
||||
static void
|
||||
log_reportchannel(ns_client_t *client) {
|
||||
char classbuf[DNS_RDATACLASS_FORMATSIZE];
|
||||
char namebuf[DNS_NAME_FORMATSIZE];
|
||||
|
||||
client->attributes |= NS_CLIENTATTR_WANTRC;
|
||||
|
||||
if (client->view->rad != NULL &&
|
||||
dns_name_issubdomain(client->query.qname, client->view->rad))
|
||||
{
|
||||
/*
|
||||
* Don't add Report-Channel to responses at or below the
|
||||
* reporting agent domain to prevent infinite loops.
|
||||
*/
|
||||
client->attributes &= ~NS_CLIENTATTR_WANTRC;
|
||||
}
|
||||
|
||||
if (client->query.qtype != dns_rdatatype_txt ||
|
||||
client->view->rad == NULL ||
|
||||
!dns_name_israd(client->query.qname, client->view->rad))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for TCP or a good server cookie. If neither send
|
||||
* back BADCOOKIE or TC=1.
|
||||
*/
|
||||
if (!TCP(client) && !HAVECOOKIE(client)) {
|
||||
if (WANTCOOKIE(client)) {
|
||||
client->attributes |= NS_CLIENTATTR_BADCOOKIE;
|
||||
} else {
|
||||
client->attributes |= NS_CLIENTATTR_NEEDTCP;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isc_log_wouldlog(ISC_LOG_INFO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
|
||||
dns_rdataclass_format(client->view->rdclass, classbuf,
|
||||
sizeof(classbuf));
|
||||
|
||||
isc_log_write(NS_LOGCATEGORY_DRA, NS_LOGMODULE_QUERY, ISC_LOG_INFO,
|
||||
"dns-reporting-agent '%s/%s'", namebuf, classbuf);
|
||||
}
|
||||
|
||||
static void
|
||||
log_tat(ns_client_t *client) {
|
||||
char namebuf[DNS_NAME_FORMATSIZE];
|
||||
@ -11714,6 +11778,7 @@ ns_query_start(ns_client_t *client, isc_nmhandle_t *handle) {
|
||||
dns_rdatatypestats_increment(client->manager->sctx->rcvquerystats,
|
||||
qtype);
|
||||
|
||||
log_reportchannel(client);
|
||||
log_tat(client);
|
||||
|
||||
if (dns_rdatatype_ismeta(qtype)) {
|
||||
|
@ -1848,6 +1848,45 @@ ISC_RUN_TEST_IMPL(edns_client_subnet) {
|
||||
dns_rdatatype_opt, sizeof(dns_rdata_opt_t));
|
||||
}
|
||||
|
||||
ISC_RUN_TEST_IMPL(edns_rad) {
|
||||
wire_ok_t wire_ok[] = {
|
||||
/*
|
||||
* Option code with no content.
|
||||
*/
|
||||
WIRE_INVALID(0x00, 0x12, 0x00, 0x00),
|
||||
/*
|
||||
* Agent Domain = "."
|
||||
*/
|
||||
WIRE_VALID(0x00, 0x12, 0x00, 0x01, 0x00),
|
||||
/*
|
||||
* Data after name.
|
||||
*/
|
||||
WIRE_INVALID(0x00, 0x12, 0x00, 0x02, 0x00, 0x00),
|
||||
/*
|
||||
* Agent Domain = "example.com."
|
||||
*/
|
||||
WIRE_VALID(0x00, 0x12, 0x00, 13, 7, 'e', 'x', 'a', 'm', 'p',
|
||||
'l', 'e', 3, 'c', 'o', 'm', 0x00),
|
||||
/*
|
||||
* No root label at end.
|
||||
*/
|
||||
WIRE_INVALID(0x00, 0x12, 0x00, 12, 7, 'e', 'x', 'a', 'm', 'p',
|
||||
'l', 'e', 3, 'c', 'o', 'm'),
|
||||
/*
|
||||
* Truncated label.
|
||||
*/
|
||||
WIRE_INVALID(0x00, 0x12, 0x00, 11, 7, 'e', 'x', 'a', 'm', 'p',
|
||||
'l', 'e', 3, 'c', 'o'),
|
||||
/*
|
||||
* Sentinel.
|
||||
*/
|
||||
WIRE_SENTINEL()
|
||||
};
|
||||
|
||||
check_rdata(NULL, wire_ok, NULL, true, dns_rdataclass_in,
|
||||
dns_rdatatype_opt, sizeof(dns_rdata_opt_t));
|
||||
}
|
||||
|
||||
/*
|
||||
* http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt
|
||||
*
|
||||
@ -3171,6 +3210,7 @@ ISC_TEST_ENTRY(zonemd)
|
||||
|
||||
/* other tests */
|
||||
ISC_TEST_ENTRY(edns_client_subnet)
|
||||
ISC_TEST_ENTRY(edns_rad)
|
||||
ISC_TEST_ENTRY(atcname)
|
||||
ISC_TEST_ENTRY(atparent)
|
||||
ISC_TEST_ENTRY(iszonecutauth)
|
||||
|
@ -16,6 +16,7 @@ list1=$(
|
||||
| grep -E "^[[:space:]]+[^[:space:]]+_LOGCATEGORY_[^[:space:]]+([[:space:]]+=[[:space:]]+[-0-9]+)?," \
|
||||
| grep -Ev "ISC_LOGCATEGORY_(MAX|INVALID)" \
|
||||
| sed -e 's/.*LOGCATEGORY_\([A-Z_]*\).*/\1/' -e 's/^RRL$/rate-limit/' \
|
||||
-e 's/DRA/dns-reporting-agent/' \
|
||||
| tr 'A-Z' 'a-z' \
|
||||
| tr _ - \
|
||||
| sed 's/^tat$/trust-anchor-telemetry/' \
|
||||
|
Loading…
x
Reference in New Issue
Block a user