Skip to content

Allow resolving both IPv4 and IPv6 with --all-nameservers (--no-preference)#604

Merged
zakird merged 22 commits into
mainfrom
phillip/598-use-both-ipv4-and-ipv6-ns-iterative
May 27, 2026
Merged

Allow resolving both IPv4 and IPv6 with --all-nameservers (--no-preference)#604
zakird merged 22 commits into
mainfrom
phillip/598-use-both-ipv4-and-ipv6-ns-iterative

Conversation

@phillip-stephens
Copy link
Copy Markdown
Contributor

@phillip-stephens phillip-stephens commented May 25, 2026

Description

The concept of "iteration preference" was used when I added --all-nameservers. We want to discover what nameservers an IP has, and to do so we request the A or AAAA records for the nameservers. The underlying assumption here was that nameservers are unique by their domain name, and so the IPv4 or IPv6 address of them should be interchangeable.

The issue opener wanted to resolve both. To do so, I added a `--all-nameservers-all-ips

Adds a new --all-nameservers-all-ips that:

 --all-nameservers-all-ips    Acts like --all-nameservers, but in addition to querying every NS at each level, will query all listed IPs for that nameserver. So if emma.ns.cloudflare.com has 3x A and 3x AAAA records, we'll query all of them if this is true. If false, we'll query only 1 per NS.

Caveats

  • no-preference - If a user uses --all-nameservers without specifying an iteration preference, by default we'll flip a coin and pick a random one. Of course, if a scanning host only has IPv4 or only IPv6 support, we'll only query for A or AAAA records, respectively.
  • --all-nameservers-all-ips implies --all-nameservers

Other Improvements

  1. I noticed that while the code is very explicit about how it attempts to discover whether to use IPv4 and/or IPv6, we aren't very explicit to the end-user. I added a few Info logs to help with explainability.
(venv) ⋊> ~/zdns on phillip/598-use-both-ipv4-and-ipv6-ns-iterative ◦ echo "prstephens.com" | ./zdns A --verbosity=5  --all-nameservers 2>&1                                                                                                                         13:46:38
INFO[0000] User didn't specify IPv4 or IPv6 support. In this case, ZDNS will attempt to determine what mode is desired based on the IP version mode of: 
INFO[0000]  1. the provided --name-servers (if any)     
INFO[0000]  2. the provided --local-addr (if any)       
INFO[0000]  3. the addresses in the OS' default resolver config file (typically /etc/resolv.conf) 
INFO[0000] inferred IP mode from OS default resolvers file: IPv4 or IPv6 

And if a user provides a local address:

(venv) ⋊> ~/zdns on phillip/598-use-both-ipv4-and-ipv6-ns-iterative ◦ echo "prstephens.com" | ./zdns A --verbosity=5  --local-addr="192.168.0.203" 2>&1                                                                                                              13:52:02
INFO[0000] User didn't specify IPv4 or IPv6 support. In this case, ZDNS will attempt to determine what mode is desired based on the IP version mode of: 
INFO[0000]  1. the provided --name-servers (if any)     
INFO[0000]  2. the provided --local-addr (if any)       
INFO[0000]  3. the addresses in the OS' default resolver config file (typically /etc/resolv.conf) 
INFO[0000] user provided local addresses: [192.168.0.203] and therefore inferred using IP mode - IPv4 

And if a user provides nameservers

(venv) ⋊> ~/zdns on phillip/598-use-both-ipv4-and-ipv6-ns-iterative ◦ echo "prstephens.com" | ./zdns A --verbosity=5  --name-servers="8.8.8.8" 2>&1                                                                                                                 
...
INFO[0000] user didn't prescribe a specific IP version mode (IPv4/v6), but did provide nameservers. Inferred IP mode - IPv4 
INFO[0000] for non-iterative lookups, using external nameservers: 8.8.8.8:53 

Testing

In zdns_test.go, I added a very large table-driven test to cover various permutations of options. This helped me discover several bugs in how we handle ctx's and timeouts.

These skip tests for IP versions the test host doesn't support, ie. on GH runners that lack IPv6, we'll skip any tests with IPv6. For testing we attempt to establish an outbound connection to an IPv4 and IPv6 resolver to test for connectivity before testing.

Happy Path Testing

While the full output is massive, here is a summary of the Cloudflare resolvers queried from a host with IPv6 capability for dns-testing.com. Note that there's a result for each IP for ns-cloud-cN.googledomains.com for N = 1,2,3,4.

10659                 nameserver: "ns-cloud-c2.googledomains.com"
10660               ▽ result: (2) {protocol: "udp", resolver: "[2001:4860:4802:34::6c]:53"}
10661                   protocol: "udp"
10662                   resolver: "[2001:4860:4802:34::6c]:53"
10664               ▶ status: "TIMEOUT"
10665                 type: "A"
10667             ▽ [9]: (4) {nameserver: "ns-cloud-c1.googledomains.com", result: {…}, status: "TIMEOUT", type: "A"}
10668                 nameserver: "ns-cloud-c1.googledomains.com"
10669               ▽ result: (2) {protocol: "udp", resolver: "[2001:4860:4802:32::6c]:53"}
10670                   protocol: "udp"
10671                   resolver: "[2001:4860:4802:32::6c]:53"
10673                 status: "TIMEOUT"
10674                 type: "A"
10676             ▽ [10]: (4) {nameserver: "ns-cloud-c1.googledomains.com", result: {…}, status: "NOERROR", type: "A"}
10677                 nameserver: "ns-cloud-c1.googledomains.com"
10678               ▽ result: (4) {additionals: […], answers: […], protocol: "udp", resolver: "216.239.32.108:53"}
10679                 ▷ additionals: (1) [{flags: "", type: "EDNS0", udpsize: 512, version: 0}]
10687                 ▷ answers: (3) [{…}, {…}, {…}]
10710                   protocol: "udp"
10711                   resolver: "216.239.32.108:53"
10713                 status: "NOERROR"
10714                 type: "A"
10716             ▽ [11]: (4) {nameserver: "ns-cloud-c2.googledomains.com", result: {…}, status: "NOERROR", type: "A"}
10717                 nameserver: "ns-cloud-c2.googledomains.com"
10718               ▽ result: (4) {additionals: […], answers: […], protocol: "udp", resolver: "216.239.34.108:53"}
10719                 ▷ additionals: (1) [{flags: "", type: "EDNS0", udpsize: 512, version: 0}]
10727                 ▷ answers: (3) [{…}, {…}, {…}]
10750                   protocol: "udp"
10751                   resolver: "216.239.34.108:53"
10753                 status: "NOERROR"
10754                 type: "A"
10756             ▽ [12]: (4) {nameserver: "ns-cloud-c3.googledomains.com", result: {…}, status: "TIMEOUT", type: "A"}
10757                 nameserver: "ns-cloud-c3.googledomains.com"
10758               ▽ result: (2) {protocol: "udp", resolver: "[2001:4860:4802:36::6c]:53"}
10759                   protocol: "udp"
10760                   resolver: "[2001:4860:4802:36::6c]:53"
10762                 status: "TIMEOUT"
10763                 type: "A"
10765             ▽ [13]: (4) {nameserver: "ns-cloud-c4.googledomains.com", result: {…}, status: "NOERROR", type: "A"}
10766                 nameserver: "ns-cloud-c4.googledomains.com"
10767               ▽ result: (4) {additionals: […], answers: […], protocol: "udp", resolver: "216.239.38.108:53"}
10768                 ▷ additionals: (1) [{flags: "", type: "EDNS0", udpsize: 512, version: 0}]
10776                 ▷ answers: (3) [{…}, {…}, {…}]
10799                   protocol: "udp"
10800                   resolver: "216.239.38.108:53"
10802                 status: "NOERROR"
10803                 type: "A"
10805             ▽ [14]: (4) {nameserver: "ns-cloud-c3.googledomains.com", result: {…}, status: "NOERROR", type: "A"}
10806                 nameserver: "ns-cloud-c3.googledomains.com"
10807               ▽ result: (4) {additionals: […], answers: […], protocol: "udp", resolver: "216.239.36.108:53"}
10808                 ▷ additionals: (1) [{flags: "", type: "EDNS0", udpsize: 512, version: 0}]
10816                 ▷ answers: (3) [{…}, {…}, {…}]
10839                   protocol: "udp"
10840                   resolver: "216.239.36.108:53"
10842                 status: "NOERROR"
10843                 type: "A"
10845             ▽ [15]: (4) {nameserver: "ns-cloud-c4.googledomains.com", result: {…}, status: "NOERROR", type: "A"}
10846                 nameserver: "ns-cloud-c4.googledomains.com"
10847               ▽ result: (4) {additionals: […], answers: […], protocol: "udp", resolver: "[2001:4860:4802:38::6c]:53"}
10848                 ▷ additionals: (1) [{flags: "", type: "EDNS0", udpsize: 512, version: 0}]
10856                 ▷ answers: (3) [{…}, {…}, {…}]
10879                   protocol: "udp"
10880                   resolver: "[2001:4860:4802:38::6c]:53"
10882                 status: "NOERROR"
10883                 type: "A"
10888         duration: 13.382903666
10889         status: "NOERROR"
10890         timestamp: "2026-05-26T16:24:17+12:00"

Related Issues

Closes #598

This allows dual-stack machines to use A/AAAA records
interchangeably during iteration.
Validated with --all-nameservers and --iterative.
Want to add a table-driven test for all possible values of network
options to at least ensure we can return something.
… IP mode. Surface our internal logic of gleaning what mode we should be in
…ebase and use the standard one, a few lints too
…n-demand in the resolver but otherwise this msg is weird to a user with empty local addrs
@phillip-stephens phillip-stephens force-pushed the phillip/598-use-both-ipv4-and-ipv6-ns-iterative branch from 9f7f872 to f256dc7 Compare May 26, 2026 02:32
@phillip-stephens phillip-stephens marked this pull request as ready for review May 26, 2026 04:23
@phillip-stephens phillip-stephens requested a review from a team as a code owner May 26, 2026 04:23
@phillip-stephens phillip-stephens requested a review from zakird May 26, 2026 04:32
@zakird zakird merged commit e7b3ca0 into main May 27, 2026
8 of 9 checks passed
@zakird zakird deleted the phillip/598-use-both-ipv4-and-ipv6-ns-iterative branch May 27, 2026 00:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ability to use --all-nameservers with IPv4 and IPv6 at the same time

2 participants