Commit 28438393fd for bind

commit 28438393fde21788a37915ef68cc5fadcb27540b
Author: Evan Hunt <each@isc.org>
Date:   Wed Sep 17 03:13:52 2025 -0700

    Add support for DELEG (DE) flag in messages

    The DE bit is now correctly interpreted when encountered on the wire.
    dig can send the bit by specifing "dig +deflag" or "dig +de".

    In querylog output, the DE flag is indicated with the letter "G".

    When the DE flag is sent in a query, it is echoed back in the
    response to the client, similar to DO.

diff --git a/bin/dig/dig.c b/bin/dig/dig.c
index e8d83c5288..2e42bfd2ce 100644
--- a/bin/dig/dig.c
+++ b/bin/dig/dig.c
@@ -206,6 +206,8 @@ help(void) {
 	       "                 +[no]crypto         (Control display of "
 	       "cryptographic\n"
 	       "                                      fields in records)\n"
+	       "                 +[no]deflag         (Request DELEG if "
+	       "available)\n"
 	       "                 +[no]defname        (Use search list "
 	       "(+[no]search))\n"
 	       "                 +[no]dns64prefix    (Get the DNS64 prefixes "
@@ -1684,12 +1686,13 @@ plus_option(char *option, bool is_batchfile, bool *need_clone,
 		break;
 	case 'd':
 		switch (cmd[1]) {
-		case 'e': /* defname */
-			FULLCHECK("defname");
-			fprintf(stderr, ";; +[no]defname option is "
-					"deprecated; use +[no]search\n");
-			if (!lookup->trace) {
-				usesearch = state;
+		case 'e': /* deflag */
+			switch (cmd[2]) {
+			case 'f':
+			case '\0': /* +de is a synonym for +deflag */
+				FULLCHECK("deflag");
+				lookup->deflag = state;
+				break;
 			}
 			break;
 		case 'n':
diff --git a/bin/dig/dig.rst b/bin/dig/dig.rst
index 636a03d198..c57d47ab3c 100644
--- a/bin/dig/dig.rst
+++ b/bin/dig/dig.rst
@@ -337,6 +337,12 @@ abbreviation is unambiguous; for example, :option:`+cd` is equivalent to
    they are replaced by the string ``[omitted]`` or, in the DNSKEY case, the
    key ID is displayed as the replacement, e.g. ``[ key id = value ]``.

+.. option:: +deflag, +nodeflag
+
+   This option requests that DELEG records be sent if available, by setting
+   the DELEG OK (DE) bit in the OPT record in the additional section of the
+   query.
+
 .. option:: +defname, +nodefname

    This option, which is deprecated, is treated as a synonym for
diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c
index 2f73bdf436..57575a2ece 100644
--- a/bin/dig/dighost.c
+++ b/bin/dig/dighost.c
@@ -760,6 +760,7 @@ clone_lookup(dig_lookup_t *lookold, bool servers) {
 	looknew->adflag = lookold->adflag;
 	looknew->cdflag = lookold->cdflag;
 	looknew->coflag = lookold->coflag;
+	looknew->deflag = lookold->deflag;
 	looknew->raflag = lookold->raflag;
 	looknew->tcflag = lookold->tcflag;
 	looknew->print_unknown_format = lookold->print_unknown_format;
@@ -2380,13 +2381,17 @@ setup_lookup(dig_lookup_t *lookup) {

 		/* Set the EDNS flags */
 		unsigned int flags = lookup->ednsflags;
-		flags &= ~(DNS_MESSAGEEXTFLAG_DO | DNS_MESSAGEEXTFLAG_CO);
+		flags &= ~(DNS_MESSAGEEXTFLAG_DO | DNS_MESSAGEEXTFLAG_CO |
+			   DNS_MESSAGEEXTFLAG_DE);
 		if (lookup->dnssec) {
 			flags |= DNS_MESSAGEEXTFLAG_DO;
 		}
 		if (lookup->coflag) {
 			flags |= DNS_MESSAGEEXTFLAG_CO;
 		}
+		if (lookup->deflag) {
+			flags |= DNS_MESSAGEEXTFLAG_DE;
+		}

 		/* Set the EDNS UDP size */
 		if (lookup->udpsize < 0) {
diff --git a/bin/dig/dighost.h b/bin/dig/dighost.h
index 196f83284e..6213b236f9 100644
--- a/bin/dig/dighost.h
+++ b/bin/dig/dighost.h
@@ -104,9 +104,9 @@ struct dig_lookup {
 	unsigned int magic;
 	isc_refcount_t references;
 	bool aaonly, adflag, badcookie, besteffort, cdflag, cleared, coflag,
-		comments, dns64prefix, dnssec, doing_xfr, done_as_is, ednsneg,
-		expandaaaa, svcparamkeycompat, expire, fuzzing, header_only,
-		identify, /*%< Append an "on server <foo>" message */
+		comments, dns64prefix, deflag, dnssec, doing_xfr, done_as_is,
+		ednsneg, expandaaaa, svcparamkeycompat, expire, fuzzing,
+		header_only, identify,	/*%< Append "on server <foo>" message */
 		identify_previous_line, /*% Prepend a "Nameserver <foo>:"
 					   message, with newline and tab */
 		idnin, idnout, ignore, multiline, need_search, new_search,
diff --git a/bin/tests/system/digdelv/tests_dig.py b/bin/tests/system/digdelv/tests_dig.py
index b03b432431..7f4f8e105d 100644
--- a/bin/tests/system/digdelv/tests_dig.py
+++ b/bin/tests/system/digdelv/tests_dig.py
@@ -1270,3 +1270,13 @@ def test_yaml_character_values(dig, ns3):
     answer = response["ANSWER_SECTION"][0]
     strings = " ".join(quoted(i) for i in range(256))
     assert answer == f"all.yaml.example. 300 IN TXT {strings}"
+
+
+def test_deflag(dig, ns3):
+    """
+    Check that the DE flag is sent when specified.
+    """
+    result = dig(f"@{ns3.ip} +de a.example +qr")
+    assert "flags: de" in result.out
+    result = dig(f"@{ns3.ip} a.example +qr")
+    assert "flags: de" not in result.out
diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h
index 4a26d308ea..3a003c5a9a 100644
--- a/lib/dns/include/dns/message.h
+++ b/lib/dns/include/dns/message.h
@@ -103,6 +103,7 @@ enum {
 enum {
 	DNS_MESSAGEEXTFLAG_DO = 0x8000U, /* DNSSEC OK */
 	DNS_MESSAGEEXTFLAG_CO = 0x4000U, /* Compact denial of existence OK */
+	DNS_MESSAGEEXTFLAG_DE = 0x2000U, /* DELEG OK */
 };

 /*%< EDNS0 extended OPT codes */
@@ -140,7 +141,8 @@ enum {
 #define DNS_EDNS_MAX_OPTIONS DNS_OPT_COUNT + DNS_EDE_MAX_ERRORS

 #define DNS_MESSAGE_REPLYPRESERVE (DNS_MESSAGEFLAG_RD | DNS_MESSAGEFLAG_CD)
-#define DNS_MESSAGEEXTFLAG_REPLYPRESERVE (DNS_MESSAGEEXTFLAG_DO)
+#define DNS_MESSAGEEXTFLAG_REPLYPRESERVE \
+	(DNS_MESSAGEEXTFLAG_DO | DNS_MESSAGEEXTFLAG_DE)

 #define DNS_MESSAGE_HEADERLEN 12 /*%< 6 uint16_t's */

diff --git a/lib/dns/message.c b/lib/dns/message.c
index 0e5416c5ca..f61d85d88b 100644
--- a/lib/dns/message.c
+++ b/lib/dns/message.c
@@ -3668,10 +3668,14 @@ dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section,
 		if ((ps->ttl & DNS_MESSAGEEXTFLAG_CO) != 0) {
 			ADD_STRING(target, " co");
 		}
+		if ((ps->ttl & DNS_MESSAGEEXTFLAG_DE) != 0) {
+			ADD_STRING(target, " de");
+		}
 		ADD_STRING(target, "\n");
 		mbz = ps->ttl & 0xffff;
 		/* Exclude Known Flags. */
-		mbz &= ~(DNS_MESSAGEEXTFLAG_DO | DNS_MESSAGEEXTFLAG_CO);
+		mbz &= ~(DNS_MESSAGEEXTFLAG_DO | DNS_MESSAGEEXTFLAG_CO |
+			 DNS_MESSAGEEXTFLAG_DE);
 		if (mbz != 0) {
 			INDENT(style);
 			ADD_STRING(target, "MBZ: ");
@@ -4100,9 +4104,13 @@ dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section,
 		if ((ps->ttl & DNS_MESSAGEEXTFLAG_CO) != 0) {
 			ADD_STRING(target, " co");
 		}
+		if ((ps->ttl & DNS_MESSAGEEXTFLAG_DE) != 0) {
+			ADD_STRING(target, " de");
+		}
 		mbz = ps->ttl & 0xffff;
 		/* Exclude Known Flags. */
-		mbz &= ~(DNS_MESSAGEEXTFLAG_DO | DNS_MESSAGEEXTFLAG_CO);
+		mbz &= ~(DNS_MESSAGEEXTFLAG_DO | DNS_MESSAGEEXTFLAG_CO |
+			 DNS_MESSAGEEXTFLAG_DE);
 		if (mbz != 0) {
 			ADD_STRING(target, "; MBZ: ");
 			snprintf(buf, sizeof(buf), "0x%.4x", mbz);
diff --git a/lib/ns/query.c b/lib/ns/query.c
index 50a04e2160..649bfb0ae8 100644
--- a/lib/ns/query.c
+++ b/lib/ns/query.c
@@ -563,6 +563,9 @@ ns_client_log_flags(ns_client_t *client, unsigned int flags,
 	} else if (client->inner.wantcookie) {
 		isc_buffer_putuint8(&b, 'K');
 	}
+	if ((extflags & DNS_MESSAGEEXTFLAG_DE) != 0) {
+		isc_buffer_putuint8(&b, 'G');
+	}
 	isc_buffer_putuint8(&b, 0);
 }