diff --git a/CHANGES b/CHANGES
index 0b5a2de328..83680886ff 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+3993. [func] Dig now supports EDNS negotiation by default.
+ (dig +[no]ednsnegotiation). [RT #37604]
+
3992. [func] DiG can now send queries without questions
(dig +header-only). [RT #37599]
diff --git a/bin/dig/dig.c b/bin/dig/dig.c
index 2a00b058d8..725c5203dd 100644
--- a/bin/dig/dig.c
+++ b/bin/dig/dig.c
@@ -195,6 +195,7 @@ help(void) {
" +ednsflags=### (Set EDNS flag bits)\n"
" +ednsopt=###[:value] (Send specified EDNS option)\n"
" +noednsopt (Clear list of +ednsopt options)\n"
+" +[no]ednsnegotiation (Set EDNS version negotiation)\n"
" +[no]search (Set whether to use searchlist)\n"
" +[no]showsearch (Search with intermediate results)\n"
" +[no]defname (Ditto)\n"
@@ -981,6 +982,10 @@ plus_option(char *option, isc_boolean_t is_batchfile,
"ednsflags");
lookup->ednsflags = num;
break;
+ case 'n':
+ FULLCHECK("ednsnegotiation");
+ lookup->ednsneg = state;
+ break;
case 'o':
FULLCHECK("ednsopt");
if (!state) {
diff --git a/bin/dig/dig.docbook b/bin/dig/dig.docbook
index 24fba64ee2..5a376be820 100644
--- a/bin/dig/dig.docbook
+++ b/bin/dig/dig.docbook
@@ -590,6 +590,16 @@
+
+
+
+
+ Enable / disable EDNS version negotiation. By default
+ EDNS version negotiation is enabled.
+
+
+
+
diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c
index 7d418804fd..a171358f93 100644
--- a/bin/dig/dighost.c
+++ b/bin/dig/dighost.c
@@ -832,6 +832,7 @@ make_empty_lookup(void) {
#endif
looknew->ednsopts = NULL;
looknew->ednsoptscnt = 0;
+ looknew->ednsneg = ISC_TRUE;
looknew->dscp = -1;
dns_fixedname_init(&looknew->fdomain);
ISC_LINK_INIT(looknew, link);
@@ -889,6 +890,7 @@ clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
#endif
looknew->ednsopts = lookold->ednsopts;
looknew->ednsoptscnt = lookold->ednsoptscnt;
+ looknew->ednsneg = lookold->ednsneg;
#ifdef DIG_SIGCHASE
looknew->sigchase = lookold->sigchase;
#if DIG_SIGCHASE_TD
@@ -3468,6 +3470,10 @@ process_opt(dig_lookup_t *l, dns_message_t *msg) {
}
#endif
+static int
+ednsvers(dns_rdataset_t *opt) {
+ return ((opt->ttl >> 16) & 0xff);
+}
/*%
* Event handler for recv complete. Perform whatever actions are necessary,
@@ -3497,6 +3503,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
isc_region_t r;
isc_buffer_t *buf = NULL;
#endif
+ int newedns;
UNUSED(task);
INSIST(!free_now);
@@ -3728,6 +3735,25 @@ recv_done(isc_task_t *task, isc_event_t *event) {
goto udp_mismatch;
}
}
+ if (msg->rcode == dns_rcode_badvers && msg->opt != NULL &&
+ (newedns = ednsvers(msg->opt)) < l->edns && l->ednsneg) {
+ /*
+ * Add minimum EDNS version required checks here if needed.
+ */
+ if (l->comments)
+ printf(";; BADVERS, retrying with EDNS version %u.\n",
+ newedns);
+ l->edns = newedns;
+ n = requeue_lookup(l, ISC_TRUE);
+ n->origin = query->lookup->origin;
+ dns_message_destroy(&msg);
+ isc_event_free(&event);
+ clear_query(query);
+ cancel_lookup(l);
+ check_next_lookup(l);
+ UNLOCK_LOOKUP;
+ return;
+ }
if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0 &&
!l->ignore && !l->tcp_mode) {
if (l->comments)
diff --git a/bin/dig/include/dig/dig.h b/bin/dig/include/dig/dig.h
index 435e8d539c..7c23c74fb9 100644
--- a/bin/dig/include/dig/dig.h
+++ b/bin/dig/include/dig/dig.h
@@ -136,7 +136,8 @@ struct dig_lookup {
sit,
#endif
nsid, /*% Name Server ID (RFC 5001) */
- header_only;
+ header_only,
+ ednsneg;
#ifdef DIG_SIGCHASE
isc_boolean_t sigchase;
#if DIG_SIGCHASE_TD
diff --git a/bin/tests/system/ednscompliance/tests.sh b/bin/tests/system/ednscompliance/tests.sh
index ab958de00c..310f8211f6 100644
--- a/bin/tests/system/ednscompliance/tests.sh
+++ b/bin/tests/system/ednscompliance/tests.sh
@@ -48,7 +48,7 @@ status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:Unknown EDNS version ($n)"
ret=0 reason=
-$DIG -p 5300 @10.53.0.1 +norec +edns=100 soa $zone > dig.out$n
+$DIG -p 5300 @10.53.0.1 +norec +edns=100 +noednsneg soa $zone > dig.out$n
grep "status: BADVERS," dig.out$n > /dev/null || { ret=1; reason="status"; }
grep "EDNS: version: 0," dig.out$n > /dev/null || { ret=1; reason="version"; }
grep "IN.SOA." dig.out$n > /dev/null && { ret=1; reaons="soa"; }
@@ -69,7 +69,7 @@ status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:Unknown EDNS version + option ($n)"
ret=0 reason=
-$DIG -p 5300 @10.53.0.1 +norec +edns=100 +ednsopt=100 soa $zone > dig.out$n
+$DIG -p 5300 @10.53.0.1 +norec +edns=100 +noednsneg +ednsopt=100 soa $zone > dig.out$n
grep "status: BADVERS," dig.out$n > /dev/null || { ret=1; reason="status"; }
grep "EDNS: version: 0," dig.out$n > /dev/null || { ret=1; reason="version"; }
grep "; OPT=100" dig.out$n > /dev/null && { ret=1; reason="option"; }
@@ -91,7 +91,7 @@ status=`expr $status + $ret`
n=`expr $n + 1`
echo "I:Unknown EDNS version + flag ($n)"
ret=0 reason=
-$DIG -p 5300 @10.53.0.1 +norec +edns=100 +ednsflags=0x80 soa $zone > dig.out$n
+$DIG -p 5300 @10.53.0.1 +norec +edns=100 +noednsneg +ednsflags=0x80 soa $zone > dig.out$n
grep "status: BADVERS," dig.out$n > /dev/null || { ret=1; reason="status"; }
grep "EDNS: version: 0," dig.out$n > /dev/null || { ret=1; reason="version"; }
grep "EDNS:.*MBZ" dig.out$n > /dev/null > /dev/null && { ret=1; reason="mbz"; }
@@ -100,5 +100,15 @@ if [ $ret != 0 ]; then echo "I:failed $reason"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
+echo "I:DiG's EDNS negotiation ($n)"
+ret=0 reason=
+$DIG -p 5300 @10.53.0.1 +norec +edns=100 soa $zone > dig.out$n
+grep "status: NOERROR," dig.out$n > /dev/null || { ret=1; reason="status"; }
+grep "EDNS: version: 0," dig.out$n > /dev/null || { ret=1; reason="version"; }
+grep "IN.SOA." dig.out$n > /dev/null || { ret=1; reason="soa"; }
+if [ $ret != 0 ]; then echo "I:failed $reason"; fi
+status=`expr $status + $ret`
+n=`expr $n + 1`
+
echo "I:exit status: $status"
exit $status
diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml
index 52480d4e04..c88756e2f7 100644
--- a/doc/arm/notes.xml
+++ b/doc/arm/notes.xml
@@ -127,6 +127,12 @@
yet-to-be-defined EDNS flags in DNS requests.
+
+
+ dig +[no]ednsnegotiation can now be used enable /
+ disable EDNS version negotiation.
+
+
dig +header-only can now be used to send