From: Mark Andrews Date: Wed, 12 Jun 2024 22:36:32 +0000 (+1000) Subject: Return EDNS ZONEVERSION if requested X-Git-Tag: v9.21.7~34^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e9a87f038985392a91466a7412ef972cb40e0d67;p=thirdparty%2Fbind9.git Return EDNS ZONEVERSION if requested If there was an EDNS ZONEVERSION option in the DNS request and the answer was from a zone, return the zone's serial and number of labels excluding the root label with the type set to 0 (ZONE-SERIAL). --- diff --git a/lib/ns/client.c b/lib/ns/client.c index 8420dcacc00..e1cdd3492d0 100644 --- a/lib/ns/client.c +++ b/lib/ns/client.c @@ -102,6 +102,8 @@ #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 WANTZONEVERSION(x) \ + (((x)->attributes & NS_CLIENTATTR_WANTZONEVERSION) != 0) #define MANAGER_MAGIC ISC_MAGIC('N', 'S', 'C', 'm') #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, MANAGER_MAGIC) @@ -218,6 +220,17 @@ ns_client_settimeout(ns_client_t *client, unsigned int seconds) { /* XXXWPK TODO use netmgr to set timeout */ } +static void +client_zoneversion_reset(ns_client_t *client) { + if (client->zoneversion == NULL) { + return; + } + isc_mem_put(client->manager->mctx, client->zoneversion, + client->zoneversionlength); + client->zoneversion = NULL; + client->zoneversionlength = 0; +} + static void ns_client_endrequest(ns_client_t *client) { INSIST(client->state == NS_CLIENTSTATE_WORKING || @@ -258,6 +271,7 @@ ns_client_endrequest(ns_client_t *client) { dns_message_puttemprdataset(client->message, &client->opt); } + client_zoneversion_reset(client); client->signer = NULL; client->udpsize = 512; client->extflags = 0; @@ -1192,6 +1206,13 @@ no_nsid: ednsopts[count].value = ede->value; count++; } + if ((client->attributes & NS_CLIENTATTR_HAVEZONEVERSION) != 0) { + INSIST(count < DNS_EDNSOPTIONS); + ednsopts[count].code = DNS_OPT_ZONEVERSION; + ednsopts[count].length = client->zoneversionlength; + ednsopts[count].value = client->zoneversion; + count++; + } if (WANTRC(client)) { dns_name_t *rad = NULL; @@ -1609,8 +1630,7 @@ process_opt(ns_client_t *client, dns_rdataset_t *opt) { case DNS_OPT_CLIENT_SUBNET: result = process_ecs(client, &optbuf, optlen); if (result != ISC_R_SUCCESS) { - ns_client_error(client, result); - return result; + goto formerr; } ns_stats_increment( client->manager->sctx->nsstats, @@ -1638,13 +1658,24 @@ process_opt(ns_client_t *client, dns_rdataset_t *opt) { result = process_keytag(client, &optbuf, optlen); if (result != ISC_R_SUCCESS) { - ns_client_error(client, result); - return result; + goto formerr; } ns_stats_increment( client->manager->sctx->nsstats, ns_statscounter_keytagopt); break; + case DNS_OPT_ZONEVERSION: + if (optlen != 0 || WANTZONEVERSION(client)) { + result = DNS_R_FORMERR; + goto formerr; + } + ns_stats_increment( + client->manager->sctx->nsstats, + ns_statscounter_zoneversionopt); + client->attributes |= + NS_CLIENTATTR_WANTZONEVERSION; + isc_buffer_forward(&optbuf, optlen); + break; default: ns_stats_increment( client->manager->sctx->nsstats, @@ -1660,6 +1691,17 @@ process_opt(ns_client_t *client, dns_rdataset_t *opt) { client->attributes |= NS_CLIENTATTR_WANTOPT; return result; + +formerr: + if (result == DNS_R_FORMERR || result == DNS_R_OPTERR) { + result = ns_client_addopt(client, client->message, + &client->opt); + if (result == ISC_R_SUCCESS) { + result = DNS_R_FORMERR; + } + } + ns_client_error(client, result); + return result; } static void @@ -1724,6 +1766,7 @@ ns__client_put_cb(void *client0) { */ ns_query_free(client); dns_ede_invalidate(&client->edectx); + client_zoneversion_reset(client); client->magic = 0; diff --git a/lib/ns/include/ns/client.h b/lib/ns/include/ns/client.h index 86d1ff5b6e7..773647a2eab 100644 --- a/lib/ns/include/ns/client.h +++ b/lib/ns/include/ns/client.h @@ -224,6 +224,8 @@ struct ns_client { ISC_LINK(ns_client_t) rlink; unsigned char cookie[8]; uint32_t expire; + unsigned char *zoneversion; + uint32_t zoneversionlength; unsigned char *keytag; uint16_t keytag_len; @@ -241,26 +243,28 @@ struct ns_client { #define NS_CLIENT_MAGIC ISC_MAGIC('N', 'S', 'C', 'c') #define NS_CLIENT_VALID(c) ISC_MAGIC_VALID(c, NS_CLIENT_MAGIC) -#define NS_CLIENTATTR_TCP 0x00001 -#define NS_CLIENTATTR_RA 0x00002 /*%< Client gets recursive service */ -#define NS_CLIENTATTR_PKTINFO 0x00004 /*%< pktinfo is valid */ -#define NS_CLIENTATTR_MULTICAST 0x00008 /*%< recv'd from multicast */ -#define NS_CLIENTATTR_WANTDNSSEC 0x00010 /*%< include dnssec records */ -#define NS_CLIENTATTR_WANTNSID 0x00020 /*%< include nameserver ID */ +#define NS_CLIENTATTR_TCP 0x000001 +#define NS_CLIENTATTR_RA 0x000002 /*%< Client gets recursive service */ +#define NS_CLIENTATTR_PKTINFO 0x000004 /*%< pktinfo is valid */ +#define NS_CLIENTATTR_MULTICAST 0x000008 /*%< recv'd from multicast */ +#define NS_CLIENTATTR_WANTDNSSEC 0x000010 /*%< include dnssec records */ +#define NS_CLIENTATTR_WANTNSID 0x000020 /*%< include nameserver ID */ #define NS_CLIENTATTR_BADCOOKIE \ - 0x00040 /*%< Presented cookie is bad/out-of-date */ -#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 */ -#define NS_CLIENTATTR_WANTEXPIRE 0x00800 /*%< return seconds to expire */ -#define NS_CLIENTATTR_HAVEEXPIRE 0x01000 /*%< return seconds to expire */ -#define NS_CLIENTATTR_WANTOPT 0x02000 /*%< add opt to reply */ -#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_NEEDTCP 0x40000 /*%< send TC=1 */ + 0x000040 /*%< Presented cookie is bad/out-of-date */ +#define NS_CLIENTATTR_WANTRC 0x000080 /*%< include Report-Channel */ +#define NS_CLIENTATTR_WANTAD 0x000100 /*%< want AD in response if possible */ +#define NS_CLIENTATTR_WANTCOOKIE 0x000200 /*%< return a COOKIE */ +#define NS_CLIENTATTR_HAVECOOKIE 0x000400 /*%< has a valid COOKIE */ +#define NS_CLIENTATTR_WANTEXPIRE 0x000800 /*%< return seconds to expire */ +#define NS_CLIENTATTR_HAVEEXPIRE 0x001000 /*%< return seconds to expire */ +#define NS_CLIENTATTR_WANTOPT 0x002000 /*%< add opt to reply */ +#define NS_CLIENTATTR_HAVEECS 0x004000 /*%< received an ECS option */ +#define NS_CLIENTATTR_WANTPAD 0x008000 /*%< pad reply */ +#define NS_CLIENTATTR_USEKEEPALIVE 0x010000 /*%< use TCP keepalive */ +#define NS_CLIENTATTR_NOSETFC 0x020000 /*%< don't set servfail cache */ +#define NS_CLIENTATTR_NEEDTCP 0x040000 /*%< send TC=1 */ +#define NS_CLIENTATTR_WANTZONEVERSION 0x100000 /*%< return zoneversion */ +#define NS_CLIENTATTR_HAVEZONEVERSION 0x200000 /*%< return zoneversion */ /* * Flag to use with the SERVFAIL cache to indicate diff --git a/lib/ns/query.c b/lib/ns/query.c index c5e07bfecd7..218900dcaf4 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -7938,6 +7938,55 @@ query_getexpire(query_ctx_t *qctx) { } } +/* + * Set the zone version, if requested, when answering from a secondary, + * mirror, or primary zone. + */ +static void +query_getzoneversion(query_ctx_t *qctx) { + CCTRACE(ISC_LOG_DEBUG(3), __func__); + + if (qctx->zone == NULL || !qctx->is_zone || + qctx->client->query.restarts != 0 || + (qctx->client->attributes & NS_CLIENTATTR_WANTZONEVERSION) == 0 || + (qctx->client->attributes & NS_CLIENTATTR_HAVEZONEVERSION) != 0) + { + return; + } + + switch (dns_zone_gettype(qctx->zone)) { + case dns_zone_mirror: + case dns_zone_primary: + case dns_zone_secondary: { + isc_buffer_t b; + unsigned char buf[128]; + isc_buffer_init(&b, buf, sizeof(buf)); + isc_result_t result = dns_zone_getzoneversion(qctx->zone, &b); + if (result == ISC_R_SUCCESS) { + size_t len = isc_buffer_usedlength(&b); + /* + * Sanity check zone version from database + * implementations. Minimum length and type 0 + * contraints. + */ + if (len < 2 || (buf[1] == 0 && len != 6)) { + return; + } + qctx->client->attributes |= + NS_CLIENTATTR_HAVEZONEVERSION; + INSIST(qctx->client->zoneversion == NULL); + qctx->client->zoneversion = + isc_mem_get(qctx->client->manager->mctx, len); + qctx->client->zoneversionlength = len; + memmove(qctx->client->zoneversion, buf, len); + } + break; + } + default: + break; + } +} + /*% * Fill the ANSWER section of a positive response. */ @@ -8101,6 +8150,11 @@ query_respond(query_ctx_t *qctx) { */ query_getexpire(qctx); + /* + * Set the zone version + */ + query_getzoneversion(qctx); + result = query_addanswer(qctx); if (result != ISC_R_COMPLETE) { return result;