2
0
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:
Mark Andrews 2022-11-06 14:18:44 +00:00 committed by Evan Hunt
parent 80f611afe6
commit ac1c60d87e
29 changed files with 545 additions and 76 deletions

View File

@ -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);

View File

@ -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*

View File

@ -22,6 +22,7 @@ options {
recursion no;
notify yes;
dnssec-validation no;
send-report-channel "rad.example.net";
};
view main in {

View File

@ -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

View 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;
};

View 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;
};

View 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";
};

View File

@ -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

View 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;
};

View File

@ -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
~~~~~

View File

@ -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.

View File

@ -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).

View File

@ -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>;

View File

@ -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 */

View File

@ -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.

View File

@ -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,

View File

@ -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);

View File

@ -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));
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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);
}
}
/*

View File

@ -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 },

View File

@ -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) ||

View File

@ -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

View File

@ -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)) {

View File

@ -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)

View File

@ -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/' \