package dns import ( "context" "fmt" "net" "path/filepath" "strconv" "strings" "time" miekgDNS "github.com/miekg/dns" "github.com/spf13/viper" "github.com/zeevdiukman/helper" ) type ResponseWriter = miekgDNS.ResponseWriter type Msg = miekgDNS.Msg type HandlerFunc = miekgDNS.HandlerFunc // func(miekgDNS.ResponseWriter, *miekgDNS.Msg) type DNS struct { Config Config Server Server Mux Mux Records Records Resolver Resolver } type Config struct { *viper.Viper } type Server struct { *miekgDNS.Server } type Mux struct { *miekgDNS.ServeMux } type Records struct { TypeA map[string]string } type Resolver struct { *net.Resolver } func New(filePath string) *DNS { app := &DNS{} app.ConfigInit(filePath) app.Records.TypeA = app.Config.GetStringMapString("records.type_a") app.ServerInit() app.MuxInit() app.Resolver = NewResolver("1.1.1.1:53") app.Server.Handler = app.Mux return app } func (a *DNS) Run() { a.Server.ListenAndServe() } func (a *DNS) ConfigInit(filePath string) { dir, file := filepath.Split(filePath) fe := filepath.Ext(file) ext, _ := strings.CutPrefix(fe, ".") name, _ := strings.CutSuffix(file, fe) a.Config.Viper = viper.New() a.Config.AddConfigPath(dir) a.Config.SetConfigName(name) a.Config.SetConfigType(ext) a.Config.ReadInConfig() } func (a *DNS) ServerInit() { port := a.Config.GetInt("port") a.Server.Server = &miekgDNS.Server{ Addr: ":" + strconv.Itoa(port), Net: a.Config.GetString("network"), Handler: nil, } } func (a *DNS) MuxInit() { a.Mux.ServeMux = miekgDNS.NewServeMux() } func NewResolver(DNSserverAddr string) Resolver { return Resolver{ &net.Resolver{ PreferGo: true, Dial: func(ctx context.Context, network, address string) (net.Conn, error) { d := net.Dialer{Timeout: 5 * time.Second} return d.DialContext(ctx, "udp", DNSserverAddr) }, }, } } // func (a *DNS) Handler(f func(a *DNS, w miekgDNS.ResponseWriter, r *miekgDNS.Msg)) miekgDNS.HandlerFunc { // return func(w miekgDNS.ResponseWriter, r *miekgDNS.Msg) { // f(a, w, r) // } // } func (r *Resolver) Lookup(lookupAddr string, DNSserverAddr string) string { var resp []string var err error ctx := context.Background() resp, err = r.LookupHost(ctx, lookupAddr) if err != nil { fmt.Println(err.Error()) } return resp[0] } func (a *DNS) HandleTypeA(w miekgDNS.ResponseWriter, r *miekgDNS.Msg) { t := time.Now() msg := &miekgDNS.Msg{} msg.SetReply(r) q := r.Question[0] domainName := helper.FtoD(q.Name) ip := "" if ipValue, ok := a.Records.TypeA[domainName]; ok { ip = ipValue } else { dSlices := strings.Split(domainName, ".") //check if wild card if len(dSlices) > 2 { name := dSlices[len(dSlices)-2] tld := dSlices[len(dSlices)-1] cname := name + "." + tld wildCard := "*." + cname if ipValue, ok := a.Records.TypeA[wildCard]; ok { ip = ipValue } else { //lookup ip = a.Resolver.Lookup(domainName, "1.1.1.1:53") } } } RR_Header := miekgDNS.RR_Header{ Name: miekgDNS.Fqdn(domainName), Rrtype: miekgDNS.TypeA, Class: miekgDNS.ClassINET, Ttl: 3600, } answer_typeA := &miekgDNS.A{ Hdr: RR_Header, A: net.ParseIP(ip).To4(), } msg.Authoritative = true msg.RecursionDesired = false msg.SetRcode(r, miekgDNS.RcodeSuccess) msg.Answer = append(msg.Answer, answer_typeA) tt := time.Since(t) helper.P(tt, " => ", domainName) w.WriteMsg(msg) }