func IsNotCNAMERes(rrs []dns.RR) bool {
	return !IsCnameRes(rrs)
}
Check if this slice is not the content of a CNAME response after a lookup
package main
import (
	"bytes"
	"encoding/json"
	"fmt"
	a "stream-dns/agent"
	"stream-dns/utils"
	"time"
	"github.com/google/uuid"
	"github.com/miekg/dns"
	log "github.com/sirupsen/logrus"
	bolt "go.etcd.io/bbolt"
)
const (
	Timeout             = 2000 * time.Millisecond
	TypicalResponseTime = 100 * time.Millisecond
	MaxRecursion        = 5
	MaxNameservers      = 4
)
var (
	NXDOMAIN = fmt.Errorf("NXDOMAIN")
	ErrMaxRecursion = fmt.Errorf("maximum recursion depth reached: %d", MaxRecursion)
	ErrTimeout      = fmt.Errorf("timeout expired")
)
const AllDomain = "."
type PairKeyRRraw struct {
	key    []byte
	rrsRaw []byte
}
type QuestionResolverHandler struct {
	db             *bolt.DB
	config         DnsConfig
	metricsService *a.MetricsService
	resolver       *Resolver
}
func NewQuestionResolverHandler(db *bolt.DB, config DnsConfig, ms *a.MetricsService) QuestionResolverHandler {
	return QuestionResolverHandler{
		db:             db,
		config:         config,
		metricsService: ms,
		resolver:       NewResolver(),  
	}
}
func (h *QuestionResolverHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
	requestID := uuid.New().String()
	remoteAddr := w.RemoteAddr().String()
	var rcode int
	msg := dns.Msg{}
	msg.SetReply(r)
	question := msg.Question[0]  
	if h.isALocalRecord(dns.Fqdn(question.Name)) {
		msg.Authoritative = true
		msg.RecursionAvailable = true
	}
	log.WithFields(log.Fields{
		"ip":         remoteAddr,
		"request-id": requestID,
		"domain":     question.Name,
		"qtype":      dns.TypeToString[question.Qtype],
		"qclass":     dns.ClassToString[question.Qclass],
	}).Info("Got a new DNS question")
	rcode, answers := h.resolveQuestion(question, msg.RecursionDesired)
	for _, rr := range answers {
		if rr.Header().Rrtype == question.Qtype || rr.Header().Rrtype == dns.TypeCNAME {
			msg.Answer = append(msg.Answer, rr)
		} else if rr.Header().Rrtype == dns.TypeNS {
			msg.Ns = append(msg.Ns, rr)
		}
	}
	log.WithFields(log.Fields{
		"ip":         remoteAddr,
		"request-id": requestID,
		"domain":     question.Name,
		"qtype":      dns.TypeToString[question.Qtype],
		"qclass":     dns.ClassToString[question.Qclass],
		"answers":    msg.Answer,
	}).Info("Find answer for the question")
	if len(msg.Answer) == 0 && h.isALocalRecord(question.Name) {
		msg.Authoritative = true
		zone := utils.GetZoneFromQname(question.Name)
		soa := h.getSOAForTheZone(zone)
		if soa != nil {
			msg.Ns = append(msg.Ns, soa)
		}
	}
	msg.SetRcode(r, rcode)
	err := w.WriteMsg(&msg)
	if err != nil {
		log.WithFields(log.Fields{
			"ip":         w.RemoteAddr,
			"request-id": requestID,
			"domain":     question.Name,
			"qtype":      dns.TypeToString[question.Qtype],
			"qclass":     dns.ClassToString[question.Qclass],
		}).Error(err)
	}
}
func (h *QuestionResolverHandler) resolveQuestion(question dns.Question, recursionDesired bool) (rcode int, rrs []dns.RR) {
	rrs, err := h.lookupRecord(dns.Fqdn(question.Name), question.Qtype, recursionDesired, 0)
	replaceWildcardByQnameInRRsIfThereAre(rrs, question.Name)
	rcode = dns.RcodeSuccess
	if err != nil {
		rcode = dns.RcodeServerFailure
	}
	if len(rrs) == 0 {
		rcode = dns.RcodeNameError
	}
	return
}
func (h *QuestionResolverHandler) lookupRecord(qname string, qtype uint16, recursionDesired bool, depth int) (rrs []dns.RR, err error) {
	log.Debugf("Lookup for the record %s %s", qname, dns.TypeToString[qtype])
	if depth++; depth > MaxRecursion {
		return nil, ErrMaxRecursion
	}
	if h.isALocalRecord(qname) {
		rrs, err = h.lookupRecordInLocalDB(qname, qtype)
	} else {
		rrs = h.resolver.Resolve(qname, qtype)
	}
	replaceWildcardByQnameInRRsIfThereAre(rrs, qname)
	if recursionDesired && IsCnameRes(rrs) && qtype != dns.TypeCNAME {
		tmp := rrs[0].(*dns.CNAME)
		rrstmp, err := h.lookupRecord(tmp.Target, qtype, recursionDesired, depth)
		if err != nil {
			return []dns.RR{}, err
		}
		return append(rrs, rrstmp...), err
	}
	return rrs, err
}
func (h *QuestionResolverHandler) lookupRecordInLocalDB(qname string, qtype uint16) (rrs []dns.RR, err error) {
	rawRRs, err := h.selectRawRecordInLocalDb([]byte(dns.Fqdn(qname)))
	rrs, err = mapPairKeyRawRRsIntoRR(rawRRs)
	if len(rrs) == 0 {
		wildcardQname := utils.IntoWildcardQname(dns.Fqdn(qname))
		rawRRs, err = h.selectRawRecordInLocalDb([]byte(wildcardQname))
		rrs, err = mapPairKeyRawRRsIntoRR(rawRRs)
	}
	return
}
func (h *QuestionResolverHandler) selectRawRecordInLocalDb(prefix []byte) (rawRecords PairKeyRRraw, err error) {
	err = h.db.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket(RecordBucket)
		c := bucket.Cursor()
		for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() {
			rawRecords = PairKeyRRraw{key: k, rrsRaw: v}
		}
		return nil
	})
	return
}
func (h *QuestionResolverHandler) isALocalRecord(qname string) (isLocal bool) {
	return utils.IsALocalRR(qname, h.config.Zones)
}
func (h *QuestionResolverHandler) getSOAForTheZone(zone string) (soa dns.RR) {
	log.Debug("looking for the SOA of an authority zone", zone)
	h.db.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket(RecordBucket)
		soaRaw := bucket.Get(utils.Key(zone, dns.TypeSOA))
		if soaRaw != nil {
			var soatmp []dns.SOA
			err := json.Unmarshal(soaRaw, &soatmp)
			if err != nil {
				log.WithField("zone", zone).Error(err)
				return nil
			}
			soa, err = dns.NewRR(soatmp[0].String())
			if err != nil {
				log.WithField("zone", zone).Error(err)
				return nil
			}
		} else {
			log.WithField("zone", zone).Debug("can't found SOA")
		}
		return nil
	})
	return
}
func mapPairKeyRawRRsIntoRR(pair PairKeyRRraw) (rrs []dns.RR, err error) {
	if pair.key == nil || pair.rrsRaw == nil {
		return []dns.RR{}, nil
	}
	qname, qtype := utils.ExtractQnameAndQtypeFromKey(pair.key)
	switch qtype {
	case dns.TypeA:
		var tmp []dns.A
		err = json.Unmarshal(pair.rrsRaw, &tmp)
		if err != nil {
			return
		}
		for _, val := range tmp {
			rr, err := dns.NewRR(val.String())
			if err != nil {
				return nil, err
			}
			rrs = append(rrs, rr)
		}
		break
	case dns.TypeAAAA:
		var tmp []dns.AAAA
		err = json.Unmarshal(pair.rrsRaw, &tmp)
		if err != nil {
			return
		}
		for _, val := range tmp {
			rr, err := dns.NewRR(val.String())
			if err != nil {
				return nil, err
			}
			rrs = append(rrs, rr)
		}
		break
	case dns.TypeCNAME:
		var tmp []dns.CNAME
		err = json.Unmarshal(pair.rrsRaw, &tmp)
		if err != nil {
			return
		}
		for _, val := range tmp {
			rr, err := dns.NewRR(val.String())
			if err != nil {
				return nil, err
			}
			rrs = append(rrs, rr)
		}
		break
	case dns.TypeTXT:
		var tmp []dns.TXT
		err = json.Unmarshal(pair.rrsRaw, &tmp)
		if err != nil {
			return
		}
		for _, val := range tmp {
			rr, err := dns.NewRR(val.String())
			if err != nil {
				return nil, err
			}
			rrs = append(rrs, rr)
		}
		break
	case dns.TypeSOA:
		var tmp []dns.SOA
		err = json.Unmarshal(pair.rrsRaw, &tmp)
		if err != nil {
			return
		}
		for _, val := range tmp {
			rr, err := dns.NewRR(val.String())
			if err != nil {
				return nil, err
			}
			rrs = append(rrs, rr)
		}
		break
	case dns.TypePTR:
		var tmp []dns.PTR
		err = json.Unmarshal(pair.rrsRaw, &tmp)
		if err != nil {
			return
		}
		for _, val := range tmp {
			rr, err := dns.NewRR(val.String())
			if err != nil {
				return nil, err
			}
			rrs = append(rrs, rr)
		}
		break
	case dns.TypeNS:
		var tmp []dns.NS
		err = json.Unmarshal(pair.rrsRaw, &tmp)
		if err != nil {
			return
		}
		for _, val := range tmp {
			rr, err := dns.NewRR(val.String())
			if err != nil {
				return nil, err
			}
			rrs = append(rrs, rr)
		}
		break
	default:
		return nil, fmt.Errorf("Can't unmarshall %s, his QTYPE doesn't exist or isn't supported yet", qname)
	}
	return
}