package yamlzone import ( "context" "fmt" "strconv" "github.com/coredns/caddy" "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" clog "github.com/coredns/coredns/plugin/pkg/log" "github.com/coredns/coredns/request" "github.com/miekg/dns" ) type YamlPlugin struct { Next plugin.Handler Zone *Zone Config YamlPluginConfig } type YamlPluginConfig struct { DefaultTtl uint64 } var log = clog.NewWithPlugin("yaml") func (y YamlPlugin) Name() string { return "yaml" } func (y YamlPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := request.Request{W: w, Req: r} qname := state.Name() qtype := state.Type() reply := new(dns.Msg) reply.SetReply(r) reply.Authoritative = true rcode, err := y.lookupRRs(qname, qtype, reply) if err != nil { return dns.RcodeServerFailure, fmt.Errorf("failed to lookup RRs for %s %s: %w", qname, qtype, err) } w.WriteMsg(reply) return rcode, nil } func setup(c *caddy.Controller) error { c.Next() // skip "yaml" filename := "zones.yaml" if c.NextArg() { filename = c.Val() } defaultTtl := uint64(60) if c.NextArg() { var err error defaultTtl, err = strconv.ParseUint(c.Val(), 10, 32) if err != nil { return plugin.Error("yaml", fmt.Errorf("failed to parse default TTL: %w", err)) } } zone, err := LoadZone(filename) if err != nil { return plugin.Error("yaml", fmt.Errorf("failed to load zone from %s: %w", filename, err)) } dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { return &YamlPlugin{ Next: next, Zone: zone, Config: YamlPluginConfig{ DefaultTtl: defaultTtl, }, } }) return nil } func init() { plugin.Register("yaml", setup) } func (y YamlPlugin) lookupRRs(qname string, qtype string, reply *dns.Msg) (int, error) { res, ok := y.Zone.LookupType(qname, qtype) if !ok { // NXDOMAIN reply.Rcode = dns.RcodeNameError } if res.IsReferral { reply.Authoritative = false } sections := []struct { source []NamedRecord dest *[]dns.RR }{ {res.Answer, &reply.Answer}, {res.Ns, &reply.Ns}, {res.Extra, &reply.Extra}, } for _, section := range sections { for _, record := range section.source { ttl := record.Record.Ttl if ttl == 0 { ttl = y.Config.DefaultTtl } rrString := fmt.Sprintf("%s %d %s %s", record.Name, ttl, record.Record.Type, record.Record.Value) rr, err := dns.NewRR(rrString) if err != nil { log.Errorf("Failed to parse RR string: %q", rrString) return reply.Rcode, fmt.Errorf("failed to generate RR for %s %s %s: %w", record.Name, record.Record.Type, record.Record.Value, err) } *section.dest = append(*section.dest, rr) } } return reply.Rcode, nil }