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

Allow users to specify name-servers in input regardless of nameservers/local addresses supplied with flags #437

Merged
merged 29 commits into from
Sep 5, 2024

Conversation

phillip-stephens
Copy link
Contributor

@phillip-stephens phillip-stephens commented Sep 5, 2024

Closes #436

You can't connect to a non-loopback nameserver with a loopback local address, and vice-versa. Prior to this change, lots of validation/unit tests were added to ensure that if loopback/non-loopback addresses (local addresses, name servers) were mixed in the ResolverConfig, an error would be thrown. The thinking was that after validation, we could be sure we could reach any nameserver we tried with the localAddress stored.

However, this approach doesn't work for the reasonable example:
echo "google.com,1.1.1.1" | ./zdns A when /etc/resolv.conf contains only loopback addresses

Since in the absence of user-supplied --name-servers, we use /etc/resolv.conf to fill them in, the above would fail since the Resolver would have a loopback local address, but then we'd attempt to query an internet-routable IP.

The solution I settled on was to stop checking for this loopback mismatch and just get local addresses/sockets on-demand. Users are free to mix and match as they like. For example, the following works:

$ echo "google.com,1.1.1.1" | ./zdns A --name-servers="127.0.0.53" --verbosity=5
INFO[0000] using local addresses: []
INFO[0000] for non-iterative lookups, using external nameservers: 127.0.0.53:53
INFO[0000] for iterative lookups, using nameservers: 127.0.0.53:53
...
INFO[0000] none of the user-supplied local addresses could connect to name server 1.1.1.1:53, using local address 171.67.71.209
...
{"name":"google.com","results":{"A":{"data":{"additionals":[{"flags":"","type":"EDNS0","udpsize":1232,"version":0}],"answers":[{"answer":"142.250.189.238","class":"IN","name":"google.com","ttl":268,"type":"A"}],"protocol":"udp","resolver":"1.1.1.1:53"},"duration":0.004254892,"status":"NOERROR","timestamp":"2024-09-05T19:42:58Z"}}}

And you can even mix and match loopback and non-loopback too

$ echo "google.com\nyahoo.com" | ./zdns A --name-servers="127.0.0.53,1.1.1.1"

{"name":"yahoo.com","results":{"A":{"data":{"additionals":[{"flags":"","type":"EDNS0","udpsize":65494,"version":0}],"answers":[{"answer":"98.137.11.163","class":"IN","name":"yahoo.com","ttl":1228,"type":"A"},{"answer":"74.6.231.20","class":"IN","name":"yahoo.com","ttl":1228,"type":"A"},{"answer":"74.6.143.25","class":"IN","name":"yahoo.com","ttl":1228,"type":"A"},{"answer":"74.6.231.21","class":"IN","name":"yahoo.com","ttl":1228,"type":"A"},{"answer":"74.6.143.26","class":"IN","name":"yahoo.com","ttl":1228,"type":"A"},{"answer":"98.137.11.164","class":"IN","name":"yahoo.com","ttl":1228,"type":"A"}],"protocol":"udp","resolver":"127.0.0.53:53"},"duration":0.000919296,"status":"NOERROR","timestamp":"2024-09-05T19:30:43Z"}}}
{"name":"google.com","results":{"A":{"data":{"additionals":[{"flags":"","type":"EDNS0","udpsize":1232,"version":0}],"answers":[{"answer":"142.251.32.46","class":"IN","name":"google.com","ttl":171,"type":"A"}],"protocol":"udp","resolver":"1.1.1.1:53"},"duration":0.003497767,"status":"NOERROR","timestamp":"2024-09-05T19:30:43Z"}}}

Notice we're able to use both a loopback and non-loopback nameservers just fine. This is accomplished by creating connectionInfo objects on-demand rather than at Resolver initiation. If a user never needs a loopback UDP socket, one is not created. But once it's created, it will be saved so we don't have to re-open the socket.

Reprecussions

One outcome/downside of this approach is we no longer validate if a name-server is reachable at Resolver startup` from the current local addresses. We just try it and see if it works. So the following don't error until the lookup is attempted.

In the below example, this host lacks IPv6 connectivity. We don't error at Resolver init, but in the query itself.

~/zdns on  phillip/436-name-server-loopback-bug! ⌚ 19:30:43
$ echo "google.com,2606:4700:4700::1111" | ./zdns A
{"name":"google.com","results":{"A":{"data":{"protocol":"","resolver":""},"duration":0.001083508,"error":"could not get a connection info to query nameserver [2606:4700:4700::1111]:53: unable to find default IP address to open socket: dial udp [2606:4700:4700::1111]:53: connect: network is unreachable","status":"ERROR","timestamp":"2024-09-05T19:33:30Z"}}}


~/zdns on  phillip/436-name-server-loopback-bug! ⌚ 19:33:51
$ echo "google.com" | ./zdns A --name-servers=2606:4700:4700::1111
{"name":"google.com","results":{"A":{"data":{"protocol":"","resolver":""},"duration":0.000414397,"error":"could not get a connection info to query nameserver [2606:4700:4700::1111]:53: unable to find default IP address to open socket: dial udp [2606:4700:4700::1111]:53: connect: network is unreachable","status":"ERROR","timestamp":"2024-09-05T19:33:56Z"}}}

Testing

I've run the test suite on GH, on a host without IPv6 connectivity and with, and with loopback NS's in /etc/resolv.conf and without.

Spot-checked a couple modules while varying --4/--6, --iterative, /etc/resolv.conf with + without loopback.

Performance

One area of concern is around the creation of sockets on-demand. If it's working as I intended it shouldn't matter, each resolver creates a socket of whatever type just once. Testing shows no noticeable regression compared to main.
When running make benchmark
main - 36.15 s.
branch - 31.93 s.

Note to Reviewers

Check out getConnectionInfo here, it has the "on-demand" socket creation

@phillip-stephens phillip-stephens marked this pull request as ready for review September 5, 2024 19:52
@phillip-stephens phillip-stephens requested a review from a team as a code owner September 5, 2024 19:52
@phillip-stephens phillip-stephens changed the title DRAFT - Allow users to specify name-servers in input regardless of nameservers/local addresses supplied with flags Allow users to specify name-servers in input regardless of nameservers/local addresses supplied with flags Sep 5, 2024
@zakird zakird merged commit ce54bd9 into main Sep 5, 2024
3 checks passed
@zakird zakird deleted the phillip/436-name-server-loopback-bug branch September 5, 2024 20:22
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.

[Bug] Cannot pass nameservers with domain input when /etc/resolv.conf has a loopback address
2 participants