[GH-ISSUE #2192] hickory-dns responds to dig A doesnotexist.fqdn.com. with NOERROR instead of with NXDOMAIN #915

Closed
opened 2026-03-16 00:52:12 +03:00 by kerem · 7 comments
Owner

Originally created by @japaric on GitHub (Apr 23, 2024).
Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/2192

Describe the bug
What the title says

To Reproduce

  1. Set up a local nameserver network with the following records (NOTE I used nsd 4.6.1 for all the nameservers)

dns

  1. set up hickory-dns as a resolver with root hint set to primary2.nameservers.com. (which has the . SOA record)

NOTE: hickory-dns was built with the recursor feature enabled

  • /etc.named.toml
[[zones]]
zone = "."
zone_type = "Hint"
stores = { type = "recursor", roots = "/etc/root.hints" }
enable_dnssec = false
  • /etc/root.hints
.	86400	NS	primary2.nameservers.com.
primary2.nameservers.com.	86400	A	192.168.112.4
  1. send the query dig A doesnotexist.nameservers.com. to hickory-dns
; <<>> DiG 9.18.24-1-Debian <<>> @192.168.112.5 A doesnotexist.nameservers.com.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6072
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
;; QUESTION SECTION:
;doesnotexist.nameservers.com.	IN	A

;; Query time: 6 msec
;; SERVER: 192.168.112.5#53(192.168.112.5) (UDP)
;; WHEN: Tue Apr 23 14:00:32 UTC 2024
;; MSG SIZE  rcvd: 57

Expected behavior

I don't know if the RFCs leave this scenario unspecified but both BIND (named) and unbound return NXDOMAIN.

  • named
`dig` output
; <<>> DiG 9.18.24-1-Debian <<>> @192.168.176.6 A doesnotexist.nameservers.com.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 49582
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: a016252a98546c4a010000006627c3ce6c2277687fe9a98d (good)
;; QUESTION SECTION:
;doesnotexist.nameservers.com.	IN	A

;; AUTHORITY SECTION:
nameservers.com.	10800	IN	SOA	primary2.nameservers.com. admin2.nameservers.com. 2024010101 1800 900 604800 86400

;; Query time: 3 msec
;; SERVER: 192.168.176.6#53(192.168.176.6) (UDP)
;; WHEN: Tue Apr 23 14:21:02 UTC 2024
;; MSG SIZE  rcvd: 137
  • unbound
`dig` output
; <<>> DiG 9.18.24-1-Debian <<>> @192.168.160.6 A doesnotexist.nameservers.com.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 60324
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;doesnotexist.nameservers.com.	IN	A

;; AUTHORITY SECTION:
nameservers.com.	3600	IN	SOA	primary2.nameservers.com. admin2.nameservers.com. 2024010101 1800 900 604800 86400

;; Query time: 10 msec
;; SERVER: 192.168.160.6#53(192.168.160.6) (UDP)
;; WHEN: Tue Apr 23 14:17:37 UTC 2024
;; MSG SIZE  rcvd: 109

System:

  • OS: Debian (Linux)
  • Architecture: x86_64
  • Version rust:1-slim-bookworm (Docker image)
  • rustc version: 1.77.2

Version:
Crate: hickory-dns
Version: 6334a014

Additional context
A test version of these repro steps can be found in the dnssec-tests repo


This might be related to #2099 but in this case there are no CNAMEs or wildcards

EDIT1: noted which Cargo features were enabled
EDIT2: remove _cache_size options from named.toml since they are optional settings
EDIT3: clarify that the linux distribution is Debian

Originally created by @japaric on GitHub (Apr 23, 2024). Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/2192 **Describe the bug** What the title says **To Reproduce** 1. Set up a local nameserver network with the following records (NOTE I used `nsd 4.6.1` for all the nameservers) ![dns](https://github.com/hickory-dns/hickory-dns/assets/5018213/d636984e-2ac0-4364-a259-6a0c9c79d1c1) 2. set up `hickory-dns` as a resolver with root hint set to `primary2.nameservers.com.` (which has the `. SOA` record) NOTE: `hickory-dns` was built with the `recursor` feature enabled - `/etc.named.toml` ``` toml [[zones]] zone = "." zone_type = "Hint" stores = { type = "recursor", roots = "/etc/root.hints" } enable_dnssec = false ``` - `/etc/root.hints` ``` text . 86400 NS primary2.nameservers.com. primary2.nameservers.com. 86400 A 192.168.112.4 ``` 3. send the query `dig A doesnotexist.nameservers.com.` to `hickory-dns` ``` console ; <<>> DiG 9.18.24-1-Debian <<>> @192.168.112.5 A doesnotexist.nameservers.com. ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6072 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 1232 ;; QUESTION SECTION: ;doesnotexist.nameservers.com. IN A ;; Query time: 6 msec ;; SERVER: 192.168.112.5#53(192.168.112.5) (UDP) ;; WHEN: Tue Apr 23 14:00:32 UTC 2024 ;; MSG SIZE rcvd: 57 ``` **Expected behavior** I don't know if the RFCs leave this scenario unspecified but both BIND (`named`) and `unbound` return NXDOMAIN. - `named` <details> <summary>`dig` output</summary> ``` console ; <<>> DiG 9.18.24-1-Debian <<>> @192.168.176.6 A doesnotexist.nameservers.com. ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 49582 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ; COOKIE: a016252a98546c4a010000006627c3ce6c2277687fe9a98d (good) ;; QUESTION SECTION: ;doesnotexist.nameservers.com. IN A ;; AUTHORITY SECTION: nameservers.com. 10800 IN SOA primary2.nameservers.com. admin2.nameservers.com. 2024010101 1800 900 604800 86400 ;; Query time: 3 msec ;; SERVER: 192.168.176.6#53(192.168.176.6) (UDP) ;; WHEN: Tue Apr 23 14:21:02 UTC 2024 ;; MSG SIZE rcvd: 137 ``` </details> - `unbound` <details> <summary>`dig` output</summary> ``` console ; <<>> DiG 9.18.24-1-Debian <<>> @192.168.160.6 A doesnotexist.nameservers.com. ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 60324 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ;; QUESTION SECTION: ;doesnotexist.nameservers.com. IN A ;; AUTHORITY SECTION: nameservers.com. 3600 IN SOA primary2.nameservers.com. admin2.nameservers.com. 2024010101 1800 900 604800 86400 ;; Query time: 10 msec ;; SERVER: 192.168.160.6#53(192.168.160.6) (UDP) ;; WHEN: Tue Apr 23 14:17:37 UTC 2024 ;; MSG SIZE rcvd: 109 ``` </details> **System:** - OS: Debian (Linux) - Architecture: x86_64 - Version `rust:1-slim-bookworm` (Docker image) - rustc version: 1.77.2 **Version:** Crate: `hickory-dns` Version: 6334a014 **Additional context** A test version of these repro steps can be found in [the dnssec-tests repo](https://github.com/ferrous-systems/dnssec-tests/blob/6189787d9f78c96525ba27d9eb3fb0e08b290fc5/packages/conformance-tests/src/resolver/dns/scenarios.rs#L46) --- This might be related to #2099 but in this case there are no CNAMEs or wildcards EDIT1: noted which Cargo features were enabled EDIT2: remove `_cache_size` options from `named.toml` since they are optional settings EDIT3: clarify that the linux distribution is Debian
kerem 2026-03-16 00:52:12 +03:00
Author
Owner

@bluejekyll commented on GitHub (May 4, 2024):

There's an interesting case where it's NXDOMAIN if there are no other records at that name, but if there are any, then it's supposed to be NOERROR and no record to indicate other records besides the one queried do exist at that name.

using CLI resolver from our library, I'm getting an A record at that name:

>  resolve doesnotexist.nameservers.com 
Querying for doesnotexist.nameservers.com A from udp:8.8.8.8:53, tcp:8.8.8.8:53, udp:8.8.4.4:53, tcp:8.8.4.4:53, udp:[2001:4860:4860::8888]:53, tcp:[2001:4860:4860::8888]:53, udp:[2001:4860:4860::8844]:53, tcp:[2001:4860:4860::8844]:53
Success for query doesnotexist.nameservers.com IN A
        doesnotexist.nameservers.com. 7200 IN A 208.91.197.132

Maybe this was changed after your test?

<!-- gh-comment-id:2094335482 --> @bluejekyll commented on GitHub (May 4, 2024): There's an interesting case where it's NXDOMAIN if there are no other records at that name, but if there are any, then it's supposed to be NOERROR and no record to indicate other records besides the one queried do exist at that name. using CLI resolver from our library, I'm getting an A record at that name: ```shell > resolve doesnotexist.nameservers.com Querying for doesnotexist.nameservers.com A from udp:8.8.8.8:53, tcp:8.8.8.8:53, udp:8.8.4.4:53, tcp:8.8.4.4:53, udp:[2001:4860:4860::8888]:53, tcp:[2001:4860:4860::8888]:53, udp:[2001:4860:4860::8844]:53, tcp:[2001:4860:4860::8844]:53 Success for query doesnotexist.nameservers.com IN A doesnotexist.nameservers.com. 7200 IN A 208.91.197.132 ``` Maybe this was changed after your test?
Author
Owner

@japaric commented on GitHub (May 7, 2024):

using CLI resolver from our library, I'm getting an A record at that name:

that's because that CLI resolver has internet access and access to the public DNS network. I guess something similar if I run dig @1.1.1.1 A doesnotexist.nameservers.com (note the public DNS resolver 1.1.1.1)

In contrast to that, all the nodes in the test are in a private, local network with no internet access so they never contact root servers like a.root-servers.net. the name servers in the tests do not contain a doesnotexist.nameservers.com A record; nor wildcard records that would match the A doesnotexist.nameservers.com query

<!-- gh-comment-id:2098093853 --> @japaric commented on GitHub (May 7, 2024): > using CLI resolver from our library, I'm getting an A record at that name: that's because that CLI resolver has internet access and access to the public DNS network. I guess something similar if I run `dig @1.1.1.1 A doesnotexist.nameservers.com` (note the public DNS resolver `1.1.1.1`) In contrast to that, all the nodes in the test are in a private, local network with no internet access so they never contact root servers like `a.root-servers.net`. the name servers in the tests do not contain a `doesnotexist.nameservers.com` A record; nor wildcard records that would match the `A doesnotexist.nameservers.com` query
Author
Owner

@bluejekyll commented on GitHub (May 18, 2024):

Got it. For what it's worth, DNS has this reserved the .test. TLD for testing use cases, which can be better to use as it ensures that no requests ever go to the internet for the TLD. It might (depends on what is being tested I guess) a good idea to generally use that.

<!-- gh-comment-id:2119013440 --> @bluejekyll commented on GitHub (May 18, 2024): Got it. For what it's worth, DNS has this reserved the `.test.` TLD for testing use cases, which can be better to use as it ensures that no requests ever go to the internet for the TLD. It might (depends on what is being tested I guess) a good idea to generally use that.
Author
Owner

@bluejekyll commented on GitHub (May 18, 2024):

rereading this, it does look like this should be an NXDOMAIN. I have been wanting to setup some test cases in the hickory repo itself for tests like this so that we can more easily guarantee behavior. I'm trying to figure out why we would end up with a NOERROR in this case, definitely seems like it should be NXDOMAIN. It looks like there's a bit of a recursive set of references here... I wonder if that got triggered by looping? I'd have to recreate this test case.

<!-- gh-comment-id:2119014604 --> @bluejekyll commented on GitHub (May 18, 2024): rereading this, it does look like this should be an NXDOMAIN. I have been wanting to setup some test cases in the hickory repo itself for tests like this so that we can more easily guarantee behavior. I'm trying to figure out why we would end up with a NOERROR in this case, definitely seems like it should be NXDOMAIN. It looks like there's a bit of a recursive set of references here... I wonder if that got triggered by looping? I'd have to recreate this test case.
Author
Owner

@japaric commented on GitHub (May 22, 2024):

For what it's worth, DNS has this reserved the .test. TLD for testing use cases, which can be better to use as it ensures that no requests ever go to the internet for the TLD.

this sounded like a good idea but unbound seems to be hard-coded to answer queries of the form A sub.domain.test. with

;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 22272
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;primary0.nameservers.test.	IN	A

;; AUTHORITY SECTION:
test.			10800	IN	SOA	localhost. nobody.invalid. 1 3600 1200 604800 10800

which breaks existing conformance tests in dns-tests when I try to move from com. domains to test.. RFC6761 lets caching DNS servers special-case queries about test. domains, namely:

Caching DNS servers SHOULD recognize test names as special and SHOULD NOT, by default, attempt to look up NS records for them, or otherwise query authoritative DNS servers in an attempt to resolve test names. Instead, caching DNS servers SHOULD, by default, generate immediate negative responses for all such queries.

Maybe I can use dns-test. as the TLD in the conformance tests 🤔 I don't think it'll ever be a real TLD 🤞

<!-- gh-comment-id:2125278849 --> @japaric commented on GitHub (May 22, 2024): > For what it's worth, DNS has this reserved the .test. TLD for testing use cases, which can be better to use as it ensures that no requests ever go to the internet for the TLD. this sounded like a good idea but `unbound` seems to be hard-coded to answer queries of the form `A sub.domain.test.` with ``` console ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 22272 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ;; QUESTION SECTION: ;primary0.nameservers.test. IN A ;; AUTHORITY SECTION: test. 10800 IN SOA localhost. nobody.invalid. 1 3600 1200 604800 10800 ``` which breaks existing conformance tests in dns-tests when I try to move from `com.` domains to `test.`. RFC6761 lets caching DNS servers special-case queries about `test.` domains, namely: > Caching DNS servers SHOULD recognize test names as special and SHOULD NOT, by default, attempt to look up NS records for them, or otherwise query authoritative DNS servers in an attempt to resolve test names. Instead, caching DNS servers SHOULD, by default, generate immediate negative responses for all such queries. Maybe I can use `dns-test.` as the TLD in the conformance tests :thinking: I don't think it'll ever be a real TLD :crossed_fingers:
Author
Owner

@bluejekyll commented on GitHub (May 22, 2024):

yeah, I was just calling it out as a concern. I'm not sure how I handle .test in hickory at the moment. It's probably not worth changing it.

<!-- gh-comment-id:2125756255 --> @bluejekyll commented on GitHub (May 22, 2024): yeah, I was just calling it out as a concern. I'm not sure how I handle `.test` in hickory at the moment. It's probably not worth changing it.
Author
Owner

@marcus0x62 commented on GitHub (Oct 20, 2024):

This is fixed with #2502.

<!-- gh-comment-id:2425150942 --> @marcus0x62 commented on GitHub (Oct 20, 2024): This is fixed with #2502.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/hickory-dns#915
No description provided.