// lookupNameservers returns the authoritative nameservers for the given fqdn. func lookupNameservers(fqdn string) ([]string, error) { var authoritativeNss []string r, err := dnsQuery(fqdn, dns.TypeNS, recursiveNameserver, true) if err != nil { return nil, err } for _, rr := range r.Answer { if ns, ok := rr.(*dns.NS); ok { authoritativeNss = append(authoritativeNss, strings.ToLower(ns.Ns)) } } if len(authoritativeNss) > 0 { return authoritativeNss, nil } // Strip of the left most label to get the parent domain. offset, _ := dns.NextLabel(fqdn, 0) next := fqdn[offset:] if dns.CountLabel(next) < 2 { return nil, fmt.Errorf("Could not determine authoritative nameservers") } return lookupNameservers(next) }
// needFromPunycode returns true for strings that require punycode decoding. func needFromPunycode(s string) bool { if s == "." { return false } off := 0 end := false pl := len(_PREFIX) sl := len(s) // If s starts with _PREFIX. if sl > pl && s[off:off+pl] == _PREFIX { return true } for { // Find the part after the next ".". off, end = dns.NextLabel(s, off) if end { return false } // If this parts starts with _PREFIX. if sl-off > pl && s[off:off+pl] == _PREFIX { return true } } }
// ClosestEncloser returns the closest encloser for rr. func (z *Zone) ClosestEncloser(qname string, qtype uint16) string { // tree/tree.go does not store a parent *Node pointer, so we can't // just follow up the tree. TODO(miek): fix. offset, end := dns.NextLabel(qname, 0) for !end { elem, _ := z.Tree.Search(qname, qtype) if elem != nil { return elem.Name() } qname = qname[offset:] offset, end = dns.NextLabel(qname, offset) } return z.Apex.SOA.Header().Name }
func (zs *ZoneStore) match(q string, t uint16) (*Zone, string) { zs.RLock() defer zs.RUnlock() var zone *Zone var name string b := make([]byte, len(q)) // worst case, one label of length q off := 0 end := false for { l := len(q[off:]) for i := 0; i < l; i++ { b[i] = q[off+i] if b[i] >= 'A' && b[i] <= 'Z' { b[i] |= ('a' - 'A') } } if z, ok := zs.store[string(b[:l])]; ok { // 'causes garbage, might want to change the map key if t != dns.TypeDS { return &z, string(b[:l]) } // Continue for DS to see if we have a parent too, if so delegate to the parent zone = &z name = string(b[:l]) } off, end = dns.NextLabel(q, off) if end { break } } return zone, name }
// NewMX returns a new MX record based on the Service. func (s *Service) NewMX(name string) *dns.MX { host := dns.Fqdn(s.Host) offset, end := 0, false for i := 0; i < s.TargetStrip; i++ { offset, end = dns.NextLabel(host, offset) } if end { // We overshot the name, use the orignal one. offset = 0 } host = host[offset:] return &dns.MX{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: s.Ttl}, Preference: uint16(s.Priority), Mx: host} }
// NewSRV returns a new SRV record based on the Service. func (s *Service) NewSRV(name string, weight uint16) *dns.SRV { host := dns.Fqdn(s.Host) offset, end := 0, false for i := 0; i < s.TargetStrip; i++ { offset, end = dns.NextLabel(host, offset) } if end { // We overshot the name, use the orignal one. offset = 0 } host = host[offset:] return &dns.SRV{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, Class: dns.ClassINET, Ttl: s.Ttl}, Priority: uint16(s.Priority), Weight: weight, Port: uint16(s.Port), Target: host} }
// targetStrip strips "targetstrip" labels from the left side of the fully qualified name. func targetStrip(name string, targetStrip int) string { if targetStrip == 0 { return name } offset, end := 0, false for i := 0; i < targetStrip; i++ { offset, end = dns.NextLabel(name, offset) } if end { // We overshot the name, use the orignal one. offset = 0 } name = name[offset:] return name }
// ServeDNS is the entry point for every request to the address that s // is bound to. It acts as a multiplexer for the requests zonename as // defined in the request so that the correct zone // (configuration and middleware stack) will handle the request. func (s *Server) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { defer func() { // In case the user doesn't enable error middleware, we still // need to make sure that we stay alive up here if rec := recover(); rec != nil { DefaultErrorFunc(w, r, dns.RcodeServerFailure) } }() if m, err := middleware.Edns0Version(r); err != nil { // Wrong EDNS version, return at once. rc := middleware.RcodeToString(dns.RcodeBadVers) state := middleware.State{W: w, Req: r} metrics.Report(state, metrics.Dropped, rc, m.Len(), time.Now()) w.WriteMsg(m) return } // Execute the optional request callback if it exists if s.ReqCallback != nil && s.ReqCallback(w, r) { return } q := r.Question[0].Name b := make([]byte, len(q)) off, end := 0, false ctx := context.Background() for { l := len(q[off:]) for i := 0; i < l; i++ { b[i] = q[off+i] // normalize the name for the lookup if b[i] >= 'A' && b[i] <= 'Z' { b[i] |= ('a' - 'A') } } if h, ok := s.zones[string(b[:l])]; ok { if r.Question[0].Qtype != dns.TypeDS { rcode, _ := h.stack.ServeDNS(ctx, w, r) if RcodeNoClientWrite(rcode) { DefaultErrorFunc(w, r, rcode) } return } } off, end = dns.NextLabel(q, off) if end { break } } // Wildcard match, if we have found nothing try the root zone as a last resort. if h, ok := s.zones["."]; ok { rcode, _ := h.stack.ServeDNS(ctx, w, r) if RcodeNoClientWrite(rcode) { DefaultErrorFunc(w, r, rcode) } return } // Still here? Error out with REFUSED and some logging remoteHost := w.RemoteAddr().String() DefaultErrorFunc(w, r, dns.RcodeRefused) log.Printf("[INFO] \"%s %s %s\" - No such zone at %s (Remote: %s)", dns.Type(r.Question[0].Qtype), dns.Class(r.Question[0].Qclass), q, s.Addr, remoteHost) }