112 lines
2.6 KiB
Go
112 lines
2.6 KiB
Go
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) {
|
|
var rcode int
|
|
res, ok := y.Zone.LookupType(qname, qtype)
|
|
if !ok {
|
|
// NXDOMAIN
|
|
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
|
|
}
|
|
rr, err := dns.NewRR(fmt.Sprintf("%s %d %s %s", record.Name, ttl, record.Record.Type, record.Record.Value))
|
|
if err != nil {
|
|
return 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 rcode, nil
|
|
}
|