From 86727eae2d4c25ced4850a96f02d1e1eb74d0a65 Mon Sep 17 00:00:00 2001 From: brettlangdon Date: Sun, 17 Jan 2016 20:50:48 -0500 Subject: [PATCH] initial prototype --- .gitignore | 2 ++ Makefile | 10 ++++++++ cmd/realm/main.go | 50 +++++++++++++++++++++++++++++++++++++++ server.go | 35 ++++++++++++++++++++++++++++ zone.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 156 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 cmd/realm/main.go create mode 100644 server.go create mode 100644 zone.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3be7076 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/realm +*.zone diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..11bd510 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +realm: ./cmd/realm/main.go ./server.go ./zone.go + go build ./cmd/realm + +clean: + rm -f ./realm + +run: + go run ./cmd/realm/main.go + +.PHONY: clean run diff --git a/cmd/realm/main.go b/cmd/realm/main.go new file mode 100644 index 0000000..10cc091 --- /dev/null +++ b/cmd/realm/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "log" + + "github.com/brettlangdon/realm" + "github.com/codegangsta/cli" +) + +func main() { + var app *cli.App = cli.NewApp() + app.Name = "realm" + app.Version = "0.1.0" + app.Author = "Brett Langdon" + app.Email = "me@brett.is" + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "zone, z", + EnvVar: "REALM_ZONE", + Usage: "location to DNS zone file [required]", + }, + cli.StringFlag{ + Name: "bind, b", + EnvVar: "REALM_BIND", + Value: ":53", + Usage: "'[]:' to bind too", + }, + } + app.Action = func(c *cli.Context) { + var filename string = c.String("zone") + if filename == "" { + log.Fatal("must supply zone file via \"--zone\" flag or \"REALM_ZONE\" environment variable") + } + + var zone *realm.Zone + var err error + log.Printf("parsing zone file \"%s\"\n", filename) + zone, err = realm.ParseZone(filename) + if err != nil { + log.Fatal(err) + } + + var bind string = c.String("bind") + log.Printf("starting the server on \"%s\"\n", bind) + var server *realm.Server = realm.NewServer(bind, zone) + log.Fatal(server.ListenAndServe()) + } + + app.RunAndExitOnError() +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..c53d501 --- /dev/null +++ b/server.go @@ -0,0 +1,35 @@ +package realm + +import "github.com/miekg/dns" + +type Server struct { + server *dns.Server + zone *Zone +} + +func NewServer(listen string, zone *Zone) *Server { + var server *Server = &Server{zone: zone} + server.server = &dns.Server{ + Addr: listen, + Net: "udp", + Handler: server, + } + return server +} + +func (server *Server) ListenAndServe() error { + return server.server.ListenAndServe() +} + +func (server *Server) ServeDNS(w dns.ResponseWriter, request *dns.Msg) { + var response *dns.Msg = &dns.Msg{} + response.SetReply(request) + response.Compress = true + + for _, question := range request.Question { + var records []dns.RR = server.zone.Lookup(question.Name, question.Qtype, question.Qclass) + response.Answer = append(response.Answer, records...) + } + + w.WriteMsg(response) +} diff --git a/zone.go b/zone.go new file mode 100644 index 0000000..fa93642 --- /dev/null +++ b/zone.go @@ -0,0 +1,59 @@ +package realm + +import ( + "fmt" + "os" + + "github.com/miekg/dns" +) + +type Zone struct { + records []dns.RR +} + +func ParseZone(filename string) (*Zone, error) { + var zone *Zone + var err error + zone = &Zone{ + records: make([]dns.RR, 0), + } + + var file *os.File + file, err = os.Open(filename) + if err != nil { + return nil, fmt.Errorf("could not parse zone file \"%s\": \"%s\"", filename, err) + } + defer file.Close() + + var tokens chan *dns.Token + tokens = dns.ParseZone(file, "", "") + for token := range tokens { + if token.Error != nil { + return nil, fmt.Errorf("could not parse zone file \"%s\": \"%s\"", filename, token.Error) + } + + zone.records = append(zone.records, token.RR) + } + return zone, nil +} + +func (zone *Zone) Lookup(name string, reqType uint16, reqClass uint16) []dns.RR { + name = dns.Fqdn(name) + var records []dns.RR = make([]dns.RR, 0) + for _, record := range zone.records { + var header *dns.RR_Header = record.Header() + if header.Name != name || (header.Class != reqClass && reqClass != dns.ClassANY) { + continue + } + + if reqType == dns.TypeANY || reqType == header.Rrtype { + records = append(records, record) + } else if header.Rrtype == dns.TypeCNAME { + records = append(records, record) + var cname *dns.CNAME = record.(*dns.CNAME) + var cnameRecords []dns.RR = zone.Lookup(dns.Fqdn(cname.Target), reqType, reqClass) + records = append(records, cnameRecords...) + } + } + return records +}