Allow send-report-channel to be set at the zone level

If send-report-channel is set at the zone level, it will
be stored in the zone object and used instead of the
view-level agent-domain when constructing the EDNS
Report-Channel option.
This commit is contained in:
Mark Andrews 2024-05-15 14:10:43 +10:00 committed by Evan Hunt
parent ac1c60d87e
commit c676fd2566
13 changed files with 132 additions and 18 deletions

View file

@ -236,6 +236,7 @@ options {\n\
notify yes;\n\
notify-delay 5;\n\
notify-to-soa no;\n\
# send-report-channel <none>\n\
serial-update-method increment;\n\
sig-signing-nodes 100;\n\
sig-signing-signatures 10;\n\

View file

@ -1481,6 +1481,18 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
INSIST(result == ISC_R_SUCCESS && obj != NULL);
dns_zone_setoption(zone, DNS_ZONEOPT_NSEC3TESTZONE,
cfg_obj_asboolean(obj));
obj = NULL;
(void)cfg_map_get(zoptions, "send-report-channel", &obj);
if (obj != NULL) {
dns_fixedname_t fixed;
dns_name_t *rad = dns_fixedname_initname(&fixed);
CHECK(dns_name_fromstring(rad, cfg_obj_asstring(obj),
dns_rootname, 0, mctx));
dns_zone_setrad(zone, rad);
} else {
dns_zone_setrad(zone, NULL);
}
} else if (ztype == dns_zone_redirect) {
dns_zone_setnotifytype(zone, dns_notifytype_no);

View file

@ -36,6 +36,7 @@ view main in {
zone example.com {
type primary;
file "example.com.db";
send-report-channel "rad.example.com";
};
};

View file

@ -194,6 +194,14 @@ 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 a zone-level Report-Channel EDNS option is added to responses ($n)"
ret=0
$DIG $DIGOPTS @10.53.0.1 example.com >dig.out.test$n
grep "; Report-Channel: rad.example.com" 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

View file

@ -1691,11 +1691,14 @@ default is used.
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
When :any:`send-report-channel` is set in :namedconf:ref:`options`,
:namedconf:ref:`view`, or :namedconf:ref:`zone`, :iscman:`named` adds a
Report-Channel option to authoritative responses, using the specified
domain name as the Agent-Domain.
When it is set in :namedconf:ref:`options` or :namedconf:ref:`view` (but
*not* :namedconf:ref:`zone`), :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``.

View file

@ -50,6 +50,7 @@ zone <string> [ <class> ] {
parental-agents [ port <integer> ] [ source ( <ipv4_address> | * ) ] [ source-v6 ( <ipv6_address> | * ) ] { ( <remote-servers> | <ipv4_address> [ port <integer> ] | <ipv6_address> [ port <integer> ] ) [ key <string> ] [ tls <string> ]; ... };
parental-source ( <ipv4_address> | * );
parental-source-v6 ( <ipv6_address> | * );
send-report-channel <string>;
serial-update-method ( date | increment | unixtime );
sig-signing-nodes <integer>;
sig-signing-signatures <integer>;

View file

@ -51,6 +51,7 @@ zone <string> [ <class> ] {
request-expire <boolean>;
request-ixfr <boolean>;
request-ixfr-max-diffs <integer>;
send-report-channel <string>;
sig-signing-nodes <integer>;
sig-signing-signatures <integer>;
sig-signing-type <integer>;

View file

@ -2770,6 +2770,26 @@ dns_zone_import_skr(dns_zone_t *zone, const char *file);
* \li ISC_R_SUCCESS if there were no errors loading the SKR.
*/
void
dns_zone_setrad(dns_zone_t *zone, dns_name_t *name);
/**<
* \brief Set the per zone RAD
*
* Requires:
* \li 'zone' to be a valid zone.
* \li 'name' is NULL or a valid name.
*/
isc_result_t
dns_zone_getrad(dns_zone_t *zone, dns_name_t *name);
/**<
* \brief get the per zone RAD
*
* Requires:
* \li 'zone' to be a valid zone.
* \li 'name' is a valid name with a buffer.
*/
#if DNS_ZONE_TRACE
#define dns_zone_ref(ptr) dns_zone__ref(ptr, __func__, __FILE__, __LINE__)
#define dns_zone_unref(ptr) dns_zone__unref(ptr, __func__, __FILE__, __LINE__)

View file

@ -276,6 +276,7 @@ struct dns_zone {
isc_timer_t *timer;
isc_refcount_t irefs;
dns_name_t origin;
dns_name_t rad;
char *masterfile;
const FILE *stream; /* loading from a stream? */
ISC_LIST(dns_include_t) includes; /* Include files */
@ -1167,6 +1168,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx, unsigned int tid) {
isc_refcount_init(&zone->references, 1);
isc_refcount_init(&zone->irefs, 0);
dns_name_init(&zone->origin, NULL);
dns_name_init(&zone->rad, NULL);
isc_sockaddr_any(&zone->notifysrc4);
isc_sockaddr_any6(&zone->notifysrc6);
isc_sockaddr_any(&zone->parentalsrc4);
@ -1348,6 +1350,9 @@ zone_free(dns_zone_t *zone) {
if (dns_name_dynamic(&zone->origin)) {
dns_name_free(&zone->origin, zone->mctx);
}
if (dns_name_dynamic(&zone->rad)) {
dns_name_free(&zone->rad, zone->mctx);
}
if (zone->strnamerd != NULL) {
isc_mem_free(zone->mctx, zone->strnamerd);
}
@ -24509,3 +24514,34 @@ failure:
return (result);
}
isc_result_t
dns_zone_getrad(dns_zone_t *zone, dns_name_t *name) {
isc_result_t result = ISC_R_NOTFOUND;
REQUIRE(DNS_ZONE_VALID(zone));
REQUIRE(DNS_NAME_VALID(name));
LOCK_ZONE(zone);
if (dns_name_dynamic(&zone->rad)) {
dns_name_copy(&zone->rad, name);
result = ISC_R_SUCCESS;
}
UNLOCK_ZONE(zone);
return (result);
}
void
dns_zone_setrad(dns_zone_t *zone, dns_name_t *name) {
REQUIRE(DNS_ZONE_VALID(zone));
REQUIRE(name == NULL || DNS_NAME_VALID(name));
LOCK_ZONE(zone);
if (dns_name_dynamic(&zone->rad)) {
dns_name_free(&zone->rad, zone->mctx);
}
if (name != NULL) {
dns_name_dup(name, zone->mctx, &zone->rad);
}
UNLOCK_ZONE(zone);
}

View file

@ -2132,7 +2132,6 @@ 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 },
@ -2361,6 +2360,8 @@ static cfg_clausedef_t zone_clauses[] = {
CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
{ "parental-source-v6", &cfg_type_sockaddr6wild,
CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
{ "send-report-channel", &cfg_type_astring,
CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
{ "request-expire", &cfg_type_boolean,
CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
{ "request-ixfr", &cfg_type_boolean,

View file

@ -315,6 +315,9 @@ ns_client_endrequest(ns_client_t *client) {
client->extflags = 0;
client->ednsversion = -1;
client->additionaldepth = 0;
if (dns_name_dynamic(&client->rad)) {
dns_name_free(&client->rad, client->manager->mctx);
}
dns_ecs_init(&client->ecs);
dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
@ -1236,14 +1239,20 @@ 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++;
if (WANTRC(client)) {
dns_name_t *rad = NULL;
if (dns_name_dynamic(&client->rad)) {
rad = &client->rad;
} else if (view != NULL && view->rad != NULL) {
rad = view->rad;
}
if (rad != NULL && !dns_name_equal(rad, dns_rootname)) {
INSIST(count < DNS_EDNSOPTIONS);
ednsopts[count].code = DNS_OPT_REPORT_CHANNEL;
ednsopts[count].length = rad->length;
ednsopts[count].value = rad->ndata;
count++;
}
}
/* Padding must be added last */
@ -2568,6 +2577,7 @@ ns__client_setup(ns_client_t *client, ns_clientmgr_t *mgr, bool new) {
client->udpsize = 512;
client->ednsversion = -1;
dns_name_init(&client->signername, NULL);
dns_name_init(&client->rad, NULL);
dns_ecs_init(&client->ecs);
isc_sockaddr_any(&client->formerrcache.addr);
client->formerrcache.time = 0;

View file

@ -197,6 +197,8 @@ struct ns_client {
isc_buffer_t *buffer;
isc_buffer_t tbuffer;
dns_name_t rad; /* Zone rad domain */
isc_sockaddr_t peeraddr;
bool peeraddr_valid;
isc_netaddr_t destaddr;

View file

@ -5518,12 +5518,30 @@ ns__query_start(query_ctx_t *qctx) {
if (qctx->is_zone) {
qctx->authoritative = true;
if (qctx->zone != NULL) {
if (dns_zone_gettype(qctx->zone) == dns_zone_mirror) {
dns_fixedname_t fixed;
dns_name_t *rad;
switch (dns_zone_gettype(qctx->zone)) {
case dns_zone_mirror:
qctx->authoritative = false;
}
if (dns_zone_gettype(qctx->zone) == dns_zone_staticstub)
{
break;
case dns_zone_staticstub:
qctx->is_staticstub_zone = true;
break;
case dns_zone_primary:
case dns_zone_secondary:
rad = dns_fixedname_initname(&fixed);
if (!dns_name_dynamic(&qctx->client->rad) &&
dns_zone_getrad(qctx->zone, rad) ==
ISC_R_SUCCESS)
{
dns_name_dup(
rad,
qctx->client->manager->mctx,
&qctx->client->rad);
}
break;
default:
break;
}
}
}