commit - ded9109ce8770cd39b01c1fb5a8afdc1bd1fc335
commit + a5e7854c436639d68f9ccb98f3168d94cdd5eb0b
blob - 4b1c81b5517c84971733bfca0c64fb78887e973e
blob + ca9087535c873b2e3b6c49b86ed6a39ac8dd432b
--- cmd/recursor/recursor.go
+++ cmd/recursor/recursor.go
import (
"os"
"fmt"
- "net"
- "strings"
- "sync"
"golang.org/x/net/dns/dnsmessage"
+ "sync"
+
"olowe.co/dns"
)
-const rootA = "198.41.0.4"
-const rootB = "199.9.14.201"
-const rootC = "192.33.4.12"
-const rootD = "199.7.91.13"
-const rootE = "192.203.230.10"
-var roots []net.IP = []net.IP{net.ParseIP(rootA), net.ParseIP(rootB), net.ParseIP(rootC)}
-
-func isIPv6(ip net.IP) bool {
- return strings.Contains(ip.String(), ":")
-}
-
-// appends the DNS port to the IP to be used in a dial string.
-func ip2dial(ip net.IP) string {
- return net.JoinHostPort(ip.String(), "domain")
-}
-
-func nextServerAddrs(resources []dnsmessage.Resource) []net.IP {
- var next []net.IP
- for _, r := range resources {
- switch b := r.Body.(type) {
- case *dnsmessage.AResource:
- next = append(next, net.IP(b.A[:]))
- case *dnsmessage.AAAAResource:
- next = append(next, net.IP(b.AAAA[:]))
- }
- }
- return next
-}
-
-func resolve(q dnsmessage.Question, next []net.IP) (dnsmessage.Message, error) {
- var rmsg dnsmessage.Message
- var err error
- for _, ip := range next {
- // Aussie Broadband doesn't support IPv6 yet!
- if isIPv6(ip) {
- continue
- }
- fmt.Fprintf(os.Stderr, "asking %s about %s\n", ip, q.Name)
- rmsg, err = dns.Ask(q, ip2dial(ip))
- if rmsg.Header.Authoritative {
- return rmsg, err
- } else if rmsg.Header.RCode == dnsmessage.RCodeSuccess && err == nil {
- break
- }
- }
- if err != nil {
- return dnsmessage.Message{}, fmt.Errorf("resolve %s: %w", q.Name, err)
- }
-
- fmt.Fprintf(os.Stderr, "no answer for %s %s, checking additionals\n", q.Name, q.Type)
- if len(rmsg.Additionals) > 0 {
- return resolve(q, nextServerAddrs(rmsg.Additionals))
- }
-
- fmt.Fprintf(os.Stderr, "no additionals for %s %s, checking authorities\n", q.Name, q.Type)
- if len(rmsg.Authorities) > 0 {
- for _, a := range rmsg.Authorities {
- switch b := a.Body.(type) {
- case *dnsmessage.NSResource:
- newq := dnsmessage.Question{Name: b.NS, Type: dnsmessage.TypeA, Class: q.Class}
- rmsg, err = resolveFromRoot(newq)
- if err != nil {
- continue
- }
- if len(rmsg.Answers) > 0 {
- return resolve(q, nextServerAddrs(rmsg.Answers))
- }
- return resolve(q, nextServerAddrs(rmsg.Additionals))
- }
- }
- }
-
- return rmsg, nil
- // return rmsg, fmt.Errorf("resolve %s %s: no more servers to query", q.Name, q.Type)
-}
-
-func resolveFromRoot(q dnsmessage.Question) (dnsmessage.Message, error) {
- return resolve(q, roots)
-}
-
func handler(w dns.ResponseWriter, qmsg *dnsmessage.Message) {
var rmsg dnsmessage.Message
rmsg.Header.ID = qmsg.Header.ID
}
cache.RUnlock()
- resolved, err := resolveFromRoot(q)
+ resolved, err := dns.ResolveFromRoot(q)
if err != nil {
fmt.Fprintln(os.Stderr, err)
rmsg.Header.RCode = dnsmessage.RCodeServerFailure
blob - /dev/null
blob + 39c88b25d1be4259e854be48c3c550445493d4d4 (mode 644)
--- /dev/null
+++ resolve.go
+package dns
+
+import (
+ "fmt"
+ "net"
+ "strings"
+ "golang.org/x/net/dns/dnsmessage"
+)
+
+
+// appends the DNS port to the IP to be used in a dial string.
+func ip2dial(ip net.IP) string {
+ return net.JoinHostPort(ip.String(), "domain")
+}
+
+func isIPv6(ip net.IP) bool {
+ return strings.Contains(ip.String(), ":")
+}
+
+func nextServerAddrs(resources []dnsmessage.Resource) []net.IP {
+ var next []net.IP
+ for _, r := range resources {
+ switch b := r.Body.(type) {
+ case *dnsmessage.AResource:
+ next = append(next, net.IP(b.A[:]))
+ case *dnsmessage.AAAAResource:
+ next = append(next, net.IP(b.AAAA[:]))
+ }
+ }
+ return next
+}
+
+const rootA = "198.41.0.4"
+const rootB = "199.9.14.201"
+const rootC = "192.33.4.12"
+const rootD = "199.7.91.13"
+const rootE = "192.203.230.10"
+var roots []net.IP = []net.IP{net.ParseIP(rootA), net.ParseIP(rootB), net.ParseIP(rootC)}
+
+func ResolveFromRoot(q dnsmessage.Question) (dnsmessage.Message, error) {
+ return Resolve(q, roots)
+}
+
+func Resolve(q dnsmessage.Question, next []net.IP) (dnsmessage.Message, error) {
+ var rmsg dnsmessage.Message
+ var err error
+ for _, ip := range next {
+ // Aussie Broadband doesn't support IPv6 yet!
+ if isIPv6(ip) {
+ continue
+ }
+ rmsg, err = Ask(q, ip2dial(ip))
+ if rmsg.Header.Authoritative {
+ return rmsg, err
+ } else if rmsg.Header.RCode == dnsmessage.RCodeSuccess && err == nil {
+ break
+ }
+ }
+ if err != nil {
+ return dnsmessage.Message{}, fmt.Errorf("resolve %s: %w", q.Name, err)
+ }
+
+ // no authoritative answer, so start looking for hints of who to ask next
+ if len(rmsg.Additionals) > 0 {
+ return Resolve(q, nextServerAddrs(rmsg.Additionals))
+ }
+
+ // no hints in additionals, check authorities
+ if len(rmsg.Authorities) > 0 {
+ for _, a := range rmsg.Authorities {
+ switch b := a.Body.(type) {
+ case *dnsmessage.NSResource:
+ newq := dnsmessage.Question{Name: b.NS, Type: dnsmessage.TypeA, Class: q.Class}
+ rmsg, err = ResolveFromRoot(newq)
+ if err != nil {
+ continue
+ }
+ if len(rmsg.Answers) > 0 {
+ return Resolve(q, nextServerAddrs(rmsg.Answers))
+ }
+ return Resolve(q, nextServerAddrs(rmsg.Additionals))
+ }
+ }
+ }
+
+ // No real answer, no more servers to ask; return our best guess
+ return rmsg, nil
+}