package dnsxl import ( "context" "errors" "net" "strings" ) // Result of a lookup from a single server. type Result struct { // Server is the name of the DNSxL server that produced this result. Server string // InList is true if the IP address that was lookup up is in the list. InList bool // Weight is the server's present/not present weight, as appropriate, or // 0 on error. Weight int // Reason may be set if the IP address is in the server's list, and the // server has a reason TXT record present for it. Reason string // Error is set if there was an error looking up the IP address on this // server. Error error } // server implements a single DNSxL server. It holds the server's DNS name and // the configured weights for found / not found results. type server struct { Addr string WeightInList, WeightNotInList int } // Lookup performs a (synchronous) DNSxL lookup. func (s server) Lookup(ctx context.Context, res *net.Resolver, reversed string) Result { r := Result{ Server: s.Addr, } addr := reversed + "." + s.Addr ips, err := res.LookupIP(ctx, "ip4" /* DNSxL entries are A records */, addr) switch { case isNXDOMAIN(err): r.Weight = s.WeightNotInList return r case err != nil: r.Error = err return r case len(ips) == 0: r.Weight = s.WeightNotInList return r } txts, _ := res.LookupTXT(ctx, addr) r.InList = true r.Weight = s.WeightInList r.Reason = strings.Join(txts, "\n") return r } // isNXDOMAIN catches the error that Go returns when getting an NXDOMAIN // response. This just means "not on list" in our case. func isNXDOMAIN(err error) bool { var d *net.DNSError return errors.As(err, &d) && d.IsNotFound }