]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix #1403: Inconsistency between do-nat64 and do-not-query-address
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 11 Feb 2026 15:01:30 +0000 (16:01 +0100)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 11 Feb 2026 15:01:30 +0000 (16:01 +0100)
  during retries.

doc/Changelog
doc/unbound.conf.rst
iterator/iter_utils.c
testdata/iter_nat64_donotq.rpl [new file with mode: 0644]

index 0c7f354623ef41e587253a184e8fba14106226c3..cd6848653a6729de69d05c003422e65d61e0d449 100644 (file)
@@ -1,3 +1,7 @@
+11 February 2026: Wouter
+       - Fix #1403: Inconsistency between do-nat64 and do-not-query-address
+         during retries.
+
 9 February 2026: Wouter
        - Merge #1401: Add a new build-time option for system TLS.
          The --enable-system-tls flag enables the
index 885bd8deb767265f2390255216040cb1ce6dce72..e53282ee60d795c4e314b4df47a980b3c199ee05 100644 (file)
@@ -4164,6 +4164,17 @@ servers.
     Use a specific NAT64 prefix to reach IPv4-only servers.
     The prefix length must be one of /32, /40, /48, /56, /64 or /96.
 
+    The NAT64 prefix is allowed by the
+    :ref:`do-not-query-address<unbound.conf.do-not-query-address>` option,
+    so that there is a clear outcome of addresses in both; the NAT64 prefix
+    is allowed.
+    The IPv4 address could be filtered by the
+    :ref:`do-not-query-address<unbound.conf.do-not-query-address>` option,
+    if needed.
+    Allowing the NAT64 prefix is useful when using do-not-query-address
+    for a cluster of machines that is IPv6-only and uses NAT64, but does
+    not have internet access.
+
     Default: 64:ff9b::/96 (same as :ref:`dns64-prefix<unbound.conf.dns64.dns64-prefix>`)
 
 .. _unbound.conf.dnscrypt:
index fb419f9ab32589f089eb15fd051ec855d8b81042..feb5b702b243354ba34693af4768ea48250e82ed 100644 (file)
@@ -308,9 +308,30 @@ iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
        if(a->bogus)
                return -1; /* address of server is bogus */
        if(donotq_lookup(iter_env->donotq, &a->addr, a->addrlen)) {
-               log_addr(VERB_ALGO, "skip addr on the donotquery list",
-                       &a->addr, a->addrlen);
-               return -1; /* server is on the donotquery list */
+               if(iter_env->nat64.use_nat64 &&
+                       addr_is_ip6(&a->addr, a->addrlen) &&
+                       a->addrlen == iter_env->nat64.nat64_prefix_addrlen &&
+                       addr_in_common(&a->addr, 128,
+                               &iter_env->nat64.nat64_prefix_addr,
+                               iter_env->nat64.nat64_prefix_net,
+                               iter_env->nat64.nat64_prefix_addrlen) ==
+                               iter_env->nat64.nat64_prefix_net) {
+                       /* The NAT64 is enabled, and address is IPv6, it is
+                        * in the NAT64 prefix. It is allowed.
+                        * So that in an IPv6-only cluster without internet
+                        * access, that makes the NAT64 translation continue
+                        * to work. The NAT64 prefix is allowed. */
+                       /* Otherwise, after a timeout, the already NAT64
+                        * translated address would be treated differently,
+                        * and that causes confusion. */
+                       log_addr(VERB_ALGO, "the addr is on the donotquery "
+                               "list, but allowed because it is NAT64",
+                               &a->addr, a->addrlen);
+               } else {
+                       log_addr(VERB_ALGO, "skip addr on the donotquery list",
+                               &a->addr, a->addrlen);
+                       return -1; /* server is on the donotquery list */
+               }
        }
        if(!iter_env->supports_ipv6 && addr_is_ip6(&a->addr, a->addrlen)) {
                return -1; /* there is no ip6 available */
diff --git a/testdata/iter_nat64_donotq.rpl b/testdata/iter_nat64_donotq.rpl
new file mode 100644 (file)
index 0000000..079d52e
--- /dev/null
@@ -0,0 +1,192 @@
+; config options
+server:
+       do-nat64: yes
+       nat64-prefix: 2001:db8:1234::/96
+       target-fetch-policy: "0 0 0 0 0"
+
+       ; This is like a machine that is part of a cluster of hosts that
+       ; is IPv6-only, and uses NAT64. The cluster has no internet access.
+       do-not-query-address: ::0/0
+
+       qname-minimisation: no
+
+stub-zone:
+       name: "."
+       ; Pick an address in the NAT64 prefix, so it is allowed.
+       ; other addresses would not be allowed. Or without the bugfix,
+       ; allowed depending on state machine activation sequence.
+       stub-addr: 2001:db8:1234::1
+CONFIG_END
+
+SCENARIO_BEGIN Test NAT64 transport for v4-only with do-not-query-addresses.
+
+RANGE_BEGIN 0 100
+       ADDRESS 2001:db8:1234::1
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS        FAKE.ROOT.
+SECTION ADDITIONAL
+FAKE.ROOT.     IN      AAAA 2001:db8:1234::1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+v4only. IN NS
+SECTION AUTHORITY
+v4only.        IN NS   ns.v4only.
+SECTION ADDITIONAL
+ns.v4only.     IN      A       192.0.2.1
+ENTRY_END
+
+RANGE_END
+
+; replies from NS over "NAT64"
+
+RANGE_BEGIN 0 20
+       ADDRESS 2001:db8:1234::c000:0201
+
+; A over NAT64
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+ns.v4only. IN A
+SECTION ANSWER
+ns.v4only.     IN      A       192.0.2.1
+SECTION AUTHORITY
+v4only.                IN      NS      ns.v4only.
+ENTRY_END
+
+; no AAAA
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+ns.v4only.     IN      AAAA
+SECTION AUTHORITY
+v4only.                IN      SOA     ns.v4only. host. 1 3600 300 48000 3600
+v4only.                IN      NS      ns.v4only.
+SECTION ADDITIONAL
+ns.v4only.     IN      A       192.0.2.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+v4only.                IN      NS
+SECTION ANSWER
+v4only.                IN      NS      ns.v4only.
+SECTION ADDITIONAL
+ns.v4only.     IN      A       192.0.2.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+test.v4only.   IN      A
+SECTION ANSWER
+test.v4only.   IN      A       192.0.2.2
+SECTION AUTHORITY
+v4only.                IN      NS      ns.v4only.
+SECTION ADDITIONAL
+ns.v4only.     IN      A       192.0.2.1
+ENTRY_END
+RANGE_END
+
+RANGE_BEGIN 50 100
+       ADDRESS 2001:db8:1234::c000:0201
+; no AAAA
+; The last resort lookup of the AAAA is blocked here,
+; the last resort processing is not desired, it should resolve test2
+; straight away.
+;ENTRY_BEGIN
+;MATCH opcode qtype qname
+;ADJUST copy_id
+;REPLY AA QR NOERROR
+;SECTION QUESTION
+;ns.v4only.    IN      AAAA
+;SECTION AUTHORITY
+;v4only.               IN      SOA     ns.v4only. host. 1 3600 300 48000 3600
+;v4only.               IN      NS      ns.v4only.
+;SECTION ADDITIONAL
+;ns.v4only.    IN      A       192.0.2.1
+;ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+ns.v4only.     IN      A
+SECTION ANSWER
+ns.v4only.     IN      A       192.0.2.1
+SECTION AUTHORITY
+v4only.                IN      NS      ns.v4only.
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+test2.v4only.  IN      A
+SECTION ANSWER
+test2.v4only.  IN      A       192.0.2.3
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test.v4only. IN A
+ENTRY_END
+
+STEP 20 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+test.v4only.   IN      A
+SECTION ANSWER
+test.v4only.   IN      A       192.0.2.2
+ENTRY_END
+
+; for a query where the upstream nameserver has a timeout.
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test2.v4only. IN A
+ENTRY_END
+
+; Only the test2 query is there, and it has a timeout.
+; The address is already NAT64 translated, so now that it is
+; attempted again, it is looked up in dotnotq as the ipv6 address.
+STEP 40 TIMEOUT
+
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+test2.v4only.  IN      A
+SECTION ANSWER
+test2.v4only.  IN      A       192.0.2.3
+ENTRY_END
+
+SCENARIO_END