retryabledns seems to only use the last dns resolver.
yaabdala opened this issue · 3 comments
Line 108 in 6f59767
In the Do
function, the last resolver in the list of resolvers is selected
resolver := c.resolvers[index%uint32(len(c.resolvers))]
And the client attempts to resolve against this resolver up to the max retries. However there is no logic to cycle to the next resolver if the current one fails or returns NXDOMAIN.
Currently, behavior is that if the last DNS resolver fails, the client returns failure, the other resolvers are never used. You can see this in dnsx
using a host file like
yahoo.com
007yahoo.com
with resolvers resolvers := []string{"8.8.8.8", "74.6.35.130"}
If you swap the order of the resolvers, it will fail. But using the yahoo NS server, it succeeds. I would expect if the google ns server fails, the yahoo one would be used.
Thanks
Can you provide a go snippet to reproduce this behavior?
The iteration logic is just above the line that you have mentioned:
index := atomic.AddUint32(&c.serversIndex, 1)
resolver := c.resolvers[index%uint32(len(c.resolvers))]
The instance internal counter c.serversIndex
gets incremented atomically, and the modulo between it and the number of resolvers is used as an index of the resolvers slice. If you add a log.Println within the function to dump the resolver used, you'd see something like this:
for i := 0; i < c.options.MaxRetries; i++ {
index := atomic.AddUint32(&c.serversIndex, 1)
resolver := c.resolvers[index%uint32(len(c.resolvers))]
log.Println(i, resolver)
...
Output:
2023/01/07 20:31:42 0 1.0.0.1:53
2023/01/07 20:31:42 1 8.8.8.8:53
2023/01/07 20:31:42 2 8.8.4.4:53
2023/01/07 20:31:42 3 1.1.1.1:53
2023/01/07 20:31:42 4 1.0.0.1:53
2023/01/07 20:31:42 0 8.8.8.8:53
2023/01/07 20:31:42 1 8.8.4.4:53
2023/01/07 20:31:42 2 1.1.1.1:53
2023/01/07 20:31:42 3 1.0.0.1:53
2023/01/07 20:31:42 4 8.8.8.8:53
2023/01/07 20:31:42 0 8.8.4.4:53
2023/01/07 20:31:42 1 1.1.1.1:53
2023/01/07 20:31:42 2 1.0.0.1:53
2023/01/07 20:31:42 3 8.8.8.8:53
2023/01/07 20:31:42 4 8.8.4.4:53
This might be an issue with how dnsx
implements retryabledns
but you should be able to replicate with
func main() {
dnsxOption := dnsx.DefaultOptions
resolvers := []string{"8.8.8.8", "74.6.35.130"}
dnsxOption.BaseResolvers = resolvers
dnsClient, err := dnsx.New(dnsxOption)
check(err)
fmt.Println("Lookup: yahoo.com")
result, err := dnsClient.Lookup("yahoo.com")
check_soft(err)
for idx, msg := range result {
fmt.Printf("%d: %s\n", idx+1, msg)
}
fmt.Println("Lookup: 007yahoo.com")
result, err = dnsClient.Lookup("007yahoo.com")
check_soft(err)
for idx, msg := range result {
fmt.Printf("%d: %s\n", idx+1, msg)
}
}
Result:
Lookup: yahoo.com
err: no ips found
Lookup: 007yahoo.com
1: 212.82.100.150
2: 98.136.103.23
3: 74.6.136.150
Now swap the dns resolver order resolvers := []string{"74.6.35.130", "8.8.8.8"}
Result:
Lookup: yahoo.com
1: 98.137.11.163
2: 98.137.11.164
3: 74.6.143.25
4: 74.6.231.20
5: 74.6.231.21
6: 74.6.143.26
Lookup: 007yahoo.com
err: no ips found
We can see that because 007yahoo.com
is not propagated to upstream dns servers, it's only resolved by the yahoo nameserver. When supplying multiple dns servers, I expect that all of them would be checked when performing a lookup.
Probably this problem is the same that got fixed with projectdiscovery/dnsx#288 (merged in the dev
branch):
$ go run .
Lookup: yahoo.com
1: 98.137.11.163
2: 74.6.231.20
3: 74.6.143.26
4: 74.6.143.25
5: 74.6.231.21
6: 98.137.11.164
Lookup: 007yahoo.com
1: 212.82.100.150
2: 74.6.136.150
3: 98.136.103.23