Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix edge case with --iterative and loopback external nameservers #405

Merged
merged 7 commits into from
Aug 1, 2024

Conversation

phillip-stephens
Copy link
Contributor

@phillip-stephens phillip-stephens commented Jul 23, 2024

Description

As said in comment:

	// If we're in iterative mode, we always start the DNS resolution iterative process at the root DNS servers.
 	// However, the ZDNS resolver library we'll create doesn't know that all queries will be iterative, it's designed to be able to do
 	// both iterative queries and use a recursive resolver with the same config (and crucially, the same local address). While usually fine, there's an edge case here
 	// if it is the case that we're only doing iterative queries AND the OS' configured NS's are loopback, ZDNS library
 	// will set the local address to a loopback address so the NS's are reachable.
 	// Unfortunately, this will cause the iterative queries to fail, as the root servers are not reachable from the loopback address.
 	//
 	// To prevent this, we'll check if we're in iterative mode, the user hasn't passed in the local addr/nameservers directly to ZDNS,
 	// and the OS' configured NS's are loopback.  If so, we'll set the nameservers to be our default non-loopback recursive resolvers.
 	// This prevents the edge case described above and has no effect on iterative queries since we just use the root nameservers.

Testing

Non-loopback NS in etc/resolv.conf

echo "cloudflare.com" | ./zdns A --iterative --verbosity=5
INFO[0000] using local addresses: [192.168.1.243]
INFO[0000] for non-iterative lookups, using nameservers: 192.168.1.1:53

Loopback NS in /etc/resolv.conf

echo "cloudflare.com" | ./zdns A --iterative --verbosity=5
DEBU[0000] OS external resolution nameservers are loopback and iterative mode is enabled. Using default non-loopback nameservers to prevent resolution failure edge case
INFO[0000] using local addresses: [171.67.71.209]
INFO[0000] for non-iterative lookups, using nameservers: 8.8.8.8:53, 8.8.4.4:53, 1.1.1.1:53, 1.0.0.1:53

@phillip-stephens phillip-stephens marked this pull request as ready for review July 23, 2024 14:13
@phillip-stephens phillip-stephens requested a review from a team as a code owner July 23, 2024 14:13
@phillip-stephens phillip-stephens requested a review from zakird July 23, 2024 14:13
@phillip-stephens
Copy link
Contributor Author

@zakird Know this is a bit confusing, if you think we should just remove the overwrite of the --local-addr and force the user to select correct inputs, lmk and I'll update this.

@zakird
Copy link
Member

zakird commented Jul 23, 2024

it feels a little bit weird to me to hardcode a specific DNS resolver when OSes do have a resolver they use by default. I think that we probably should stay with the behavior of most DNS tools (e.g., dig), which is to use the resolver that the OS specifies to use unless specified otherwise.

@phillip-stephens
Copy link
Contributor Author

Hmm, I can see that except wouldn't dig in +trace (equivalent to our --iterative) not use the OS defaults, but just go to the root servers?

IMO, since we're only over-riding the nameservers for external lookups when the CLI is used with --iterative, we are still consistent with dig.

@zakird
Copy link
Member

zakird commented Jul 23, 2024

We currently have the root servers as default for iterative, which is fine. I don't think that dig +trace changes this. By default dig does use the OS's name servers, as does every other utility. From their documentation:

Unless it is told to query a specific name server, dig will try each of the servers listed in /etc/resolv.conf. If no usable server addresses are found, dig will send the query to the local host.

I think that using the same logic, following what's in /etc/resolv.conf and then local host by default makes sense for ZDNS too.

@phillip-stephens
Copy link
Contributor Author

phillip-stephens commented Jul 23, 2024

Hmm, perhaps I'm not communicating it super well.

Basically, the chain of problematic events that happens is:

  1. ZDNS called from CLI in --iterative from a machine with loopback NS' in /etc/resolv.conf
  2. ZDNS the library, reads the /etc/resolv.conf and populates the ExternalNameServers with loopback nameservers from /etc/resolv.conf
  3. During Config validation, ZDNS library realizes all nameservers are loopback, so it sets the LocalAddr to a loopback address, so the nameservers should be reachable and attempt to get the config into a good state.
  4. If we were not using --iterative, this would work perfectly fine
  5. Since we're using --iterative and we now have a loopback local address, we cannot reach the root nameservers since ofc you can't use a loopback IP address to communicate to a non-loopback address. Therefore, --iterative mode errors out

My solution was for the CLI in --iterative mode, to just fill-in the ExternalNameServers with some non-loopback defaults, thus preventing the LocalAddr being set to a loopback address in the Resolver. These ExternalNameServers will never be queried in --iterative mode anyway (since we go to the root), it's just sort of a hack to not have the resolver clobber our LocalAddr with a useless loopback address.

When not using --iterative, ZDNS will use the OS configured defaults just like dig.

I couldn't think of a cleaner solution, but am open to suggestions!

@phillip-stephens phillip-stephens changed the title Set ExternalNameServers to Google's public DNS resolvers if we're performing an iterative lookup. Fix edge case with --iterative and loopback external nameservers Jul 29, 2024
@zakird zakird merged commit 67c560c into main Aug 1, 2024
3 checks passed
@zakird zakird deleted the phillip/improved-ns-selection branch August 1, 2024 15:48
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.

2 participants