From c676fd256676adf1c71e876c1e80ad18b7a1eb56 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 15 May 2024 14:10:43 +1000 Subject: [PATCH] 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. --- bin/named/config.c | 1 + bin/named/zoneconf.c | 12 +++++++++ bin/tests/system/auth/ns1/named.conf.in | 1 + bin/tests/system/auth/tests.sh | 8 ++++++ doc/arm/reference.rst | 13 +++++---- doc/misc/primary.zoneopt | 1 + doc/misc/secondary.zoneopt | 1 + lib/dns/include/dns/zone.h | 20 ++++++++++++++ lib/dns/zone.c | 36 +++++++++++++++++++++++++ lib/isccfg/namedconf.c | 3 ++- lib/ns/client.c | 26 ++++++++++++------ lib/ns/include/ns/client.h | 2 ++ lib/ns/query.c | 26 +++++++++++++++--- 13 files changed, 132 insertions(+), 18 deletions(-) diff --git a/bin/named/config.c b/bin/named/config.c index 94df346db4..3a89ef1bd2 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -236,6 +236,7 @@ options {\n\ notify yes;\n\ notify-delay 5;\n\ notify-to-soa no;\n\ +# send-report-channel \n\ serial-update-method increment;\n\ sig-signing-nodes 100;\n\ sig-signing-signatures 10;\n\ diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index 4253a73821..3c4f43e6a6 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -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); diff --git a/bin/tests/system/auth/ns1/named.conf.in b/bin/tests/system/auth/ns1/named.conf.in index 027a08851d..0f40bbbf4a 100644 --- a/bin/tests/system/auth/ns1/named.conf.in +++ b/bin/tests/system/auth/ns1/named.conf.in @@ -36,6 +36,7 @@ view main in { zone example.com { type primary; file "example.com.db"; + send-report-channel "rad.example.com"; }; }; diff --git a/bin/tests/system/auth/tests.sh b/bin/tests/system/auth/tests.sh index 591adfd9c2..7900ae0010 100644 --- a/bin/tests/system/auth/tests.sh +++ b/bin/tests/system/auth/tests.sh @@ -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 diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index c6ff719c12..ef2d15277a 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -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...._er.) to the ``dns-reporting-agent`` logging category at level ``info``. diff --git a/doc/misc/primary.zoneopt b/doc/misc/primary.zoneopt index a561aabc2d..2a37f2b926 100644 --- a/doc/misc/primary.zoneopt +++ b/doc/misc/primary.zoneopt @@ -50,6 +50,7 @@ zone [ ] { parental-agents [ port ] [ source ( | * ) ] [ source-v6 ( | * ) ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; parental-source ( | * ); parental-source-v6 ( | * ); + send-report-channel ; serial-update-method ( date | increment | unixtime ); sig-signing-nodes ; sig-signing-signatures ; diff --git a/doc/misc/secondary.zoneopt b/doc/misc/secondary.zoneopt index 83be6bb37a..a08fe56835 100644 --- a/doc/misc/secondary.zoneopt +++ b/doc/misc/secondary.zoneopt @@ -51,6 +51,7 @@ zone [ ] { request-expire ; request-ixfr ; request-ixfr-max-diffs ; + send-report-channel ; sig-signing-nodes ; sig-signing-signatures ; sig-signing-type ; diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index e30ac2f73f..cded4750bd 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -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__) diff --git a/lib/dns/zone.c b/lib/dns/zone.c index ed5763654f..188c23f6b6 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -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); +} diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index d951101525..791b47e27e 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -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, diff --git a/lib/ns/client.c b/lib/ns/client.c index 25bb7e5a8a..a1a2b4ad34 100644 --- a/lib/ns/client.c +++ b/lib/ns/client.c @@ -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; diff --git a/lib/ns/include/ns/client.h b/lib/ns/include/ns/client.h index e03bc11e6d..2c516de3b8 100644 --- a/lib/ns/include/ns/client.h +++ b/lib/ns/include/ns/client.h @@ -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; diff --git a/lib/ns/query.c b/lib/ns/query.c index 4a11ca7857..ed2662b0a7 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -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; } } }