diff --git a/cmd/realm/main.go b/cmd/realm/main.go index 10cc091..e991e53 100644 --- a/cmd/realm/main.go +++ b/cmd/realm/main.go @@ -8,6 +8,7 @@ import ( ) func main() { + // Setup our CLI app var app *cli.App = cli.NewApp() app.Name = "realm" app.Version = "0.1.0" @@ -26,12 +27,16 @@ func main() { Usage: "'[]:' to bind too", }, } + + // This action is called for all commands app.Action = func(c *cli.Context) { + // Ensure that a zone filename was provided var filename string = c.String("zone") if filename == "" { log.Fatal("must supply zone file via \"--zone\" flag or \"REALM_ZONE\" environment variable") } + // Load and parse the zone file var zone *realm.Zone var err error log.Printf("parsing zone file \"%s\"\n", filename) @@ -40,11 +45,13 @@ func main() { log.Fatal(err) } + // Create and start the server 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()) } + // Parse command arguments and run `app.Action` app.RunAndExitOnError() } diff --git a/server.go b/server.go index c53d501..9454ff7 100644 --- a/server.go +++ b/server.go @@ -2,34 +2,45 @@ package realm import "github.com/miekg/dns" +// A Server listens for DNS requests over UDP and responds with answers from the provided Zone. type Server struct { server *dns.Server zone *Zone } +// NewServer returns a new initialized *Server that will bind to listen and will look up answers from zone. func NewServer(listen string, zone *Zone) *Server { - var server *Server = &Server{zone: zone} - server.server = &dns.Server{ + var s *Server + s = &Server{zone: zone} + s.server = &dns.Server{ Addr: listen, Net: "udp", - Handler: server, + Handler: s, } - return server + return s } -func (server *Server) ListenAndServe() error { - return server.server.ListenAndServe() +// ListenAndServe will start the nameserver on the configured address. +func (s *Server) ListenAndServe() error { + return s.server.ListenAndServe() } -func (server *Server) ServeDNS(w dns.ResponseWriter, request *dns.Msg) { - var response *dns.Msg = &dns.Msg{} +// ServeDNS will be called for every DNS request to this server. +// It will attempt to provide answers to all questions from the configured zone. +func (s *Server) ServeDNS(w dns.ResponseWriter, request *dns.Msg) { + // Setup the default response + var response *dns.Msg + response = &dns.Msg{} response.SetReply(request) response.Compress = true + // Lookup answers to any of the questions for _, question := range request.Question { - var records []dns.RR = server.zone.Lookup(question.Name, question.Qtype, question.Qclass) + var records []dns.RR + records = s.zone.Lookup(question.Name, question.Qtype, question.Qclass) response.Answer = append(response.Answer, records...) } + // Respond to the request w.WriteMsg(response) } diff --git a/zone.go b/zone.go index fa93642..b2fcfd2 100644 --- a/zone.go +++ b/zone.go @@ -7,10 +7,13 @@ import ( "github.com/miekg/dns" ) +// A Zone a container for records parsed from a zone file. type Zone struct { records []dns.RR } +// ParseZone will attempt to parse a zone file from the provided filename and return a Zone. +// ParseZone will return an error if the file provided does not exist or could not be properly parsed. func ParseZone(filename string) (*Zone, error) { var zone *Zone var err error @@ -18,6 +21,7 @@ func ParseZone(filename string) (*Zone, error) { records: make([]dns.RR, 0), } + // Open the file var file *os.File file, err = os.Open(filename) if err != nil { @@ -25,6 +29,7 @@ func ParseZone(filename string) (*Zone, error) { } defer file.Close() + // Parse the file into records var tokens chan *dns.Token tokens = dns.ParseZone(file, "", "") for token := range tokens { @@ -37,21 +42,32 @@ func ParseZone(filename string) (*Zone, error) { return zone, nil } +// Lookup will find all records which we should respond with for the given name, request type, and request class. func (zone *Zone) Lookup(name string, reqType uint16, reqClass uint16) []dns.RR { name = dns.Fqdn(name) - var records []dns.RR = make([]dns.RR, 0) + var records []dns.RR + records = make([]dns.RR, 0) for _, record := range zone.records { - var header *dns.RR_Header = record.Header() + var header *dns.RR_Header + header = record.Header() + + // Skip this record if the name or class do not match if header.Name != name || (header.Class != reqClass && reqClass != dns.ClassANY) { continue } + // Collect this record if the types match or this record is a CNAME if reqType == dns.TypeANY || reqType == header.Rrtype { records = append(records, record) } else if header.Rrtype == dns.TypeCNAME { + // Append this CNAME record as a response records = append(records, record) - var cname *dns.CNAME = record.(*dns.CNAME) - var cnameRecords []dns.RR = zone.Lookup(dns.Fqdn(cname.Target), reqType, reqClass) + + // Attempt to resolve this CNAME record + var cname *dns.CNAME + cname = record.(*dns.CNAME) + var cnameRecords []dns.RR + cnameRecords = zone.Lookup(dns.Fqdn(cname.Target), reqType, reqClass) records = append(records, cnameRecords...) } }