func TestMXHandle(t *testing.T) { msg := &mdns.Msg{} zone := &config.Zone{ MX: `1 test1.mail.server 10 test2.mail.server`, } question := mdns.Question{ Name: "test.com", } mx := &mxHandler{} err := mx.Handle(msg, zone, question) assert.Nil(t, err) expectedMsg := &mdns.Msg{} rr, err := mdns.NewRR("test.com. 3600 IN MX 1 test1.mail.server") assert.Nil(t, err) expectedMsg.Answer = append(expectedMsg.Answer, rr) rr, err = mdns.NewRR("test.com. 3600 IN MX 10 test2.mail.server") assert.Nil(t, err) expectedMsg.Answer = append(expectedMsg.Answer, rr) assert.Exactly(t, msg.Answer, expectedMsg.Answer) }
func TestTXTHandle(t *testing.T) { msg := &mdns.Msg{} zone := &config.Zone{ TXT: `never gonna give you up `, } question := mdns.Question{ Name: "test.com", } txt := &txtHandler{} err := txt.Handle(msg, zone, question) assert.Nil(t, err) expectedMsg := &mdns.Msg{} rr, err := mdns.NewRR("test.com. 3600 IN TXT never gonna") assert.Nil(t, err) expectedMsg.Answer = append(expectedMsg.Answer, rr) rr, err = mdns.NewRR("test.com. 3600 IN TXT give you up") assert.Nil(t, err) expectedMsg.Answer = append(expectedMsg.Answer, rr) assert.Exactly(t, msg.Answer, expectedMsg.Answer) }
// Handle produces reply for NS question func (n *nsHandler) Handle(msg *mdns.Msg, zone *config.Zone, question mdns.Question) (err error) { for _, server := range n.config.DNS.Servers { s := strings.Join([]string{ question.Name, "3600", "IN", "NS", server.Name, }, " ") rr, err := mdns.NewRR(s) if err == nil { msg.Answer = append(msg.Answer, rr) } } for _, server := range n.config.DNS.Servers { s := strings.Join([]string{ server.Name, "3600", "IN", "A", server.IP, }, " ") rr, err := mdns.NewRR(s) if err == nil { msg.Extra = append(msg.Extra, rr) } } return }
func localRR(hostname string, services map[string]int) (map[string][]dns.RR, error) { rrs := map[string][]dns.RR{} addrs, err := net.InterfaceAddrs() if err != nil { return nil, err } for _, addr := range addrs { var ip net.IP switch v := addr.(type) { case *net.IPNet: ip = v.IP case *net.IPAddr: ip = v.IP } if ip.IsLoopback() { continue } if ip = ip.To4(); ip != nil { for _, record := range []string{ fmt.Sprintf("%s.local. 10 IN A %s", hostname, ip.String()), fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa. 10 IN PTR %s.local.", ip[3], ip[2], ip[1], ip[0], hostname), } { if rr, err := dns.NewRR(record); err != nil { return nil, err } else { rrs[rr.Header().Name] = append(rrs[rr.Header().Name], rr) } } for s, port := range services { for _, record := range []string{ fmt.Sprintf("%s.local. 60 IN PTR %s.%s.local.", s, hostname, s), fmt.Sprintf("%s.%s.local. 60 IN SRV 0 0 %d %s.local.", hostname, s, port, hostname), fmt.Sprintf("%s.%s.local. 60 IN TXT \"\"", hostname, s), fmt.Sprintf("_services._dns-sd._udp.local. 60 IN PTR %s.local.", s), } { if rr, err := dns.NewRR(record); err != nil { return nil, err } else { rrs[rr.Header().Name] = append(rrs[rr.Header().Name], rr) } } } } } return rrs, nil }
func getRecord(domain string, rtype uint16) (rr dns.RR, err error) { if *debug { Log.Printf("getRecord: domain: %s, resource type: %d\n", domain, rtype) } key, _ := getKey(domain, rtype) var v []byte err = bdb.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(rrBucket)) v = b.Get([]byte(key)) if string(v) == "" { e := errors.New("Record not found, key: " + key) Log.Println(e.Error()) return e } return nil }) if err == nil { rr, err = dns.NewRR(string(v)) } return rr, err }
func mustNewRR(tb testing.TB, s string) dns.RR { rr, err := dns.NewRR(s) if err != nil { tb.Fatalf("invalid RR %q: %v", s, err) } return rr }
func mustParseRR(s string) dns.RR { rr, err := dns.NewRR(s) if err != nil { panic(err) } return rr }
func TestSOAHandle(t *testing.T) { saved := now now = func() int64 { return 0 } defer func() { now = saved }() msg := &mdns.Msg{} question := mdns.Question{ Name: "test.com", } soa := NewSOAHandler("kitty") err := soa.Handle(msg, nil, question) assert.Nil(t, err) expectedMsg := &mdns.Msg{} rr, err := mdns.NewRR("test.com. 3600 IN SOA kitty admin.test.com 0 10000 2400 604800 3600") assert.Nil(t, err) expectedMsg.Answer = append(expectedMsg.Answer, rr) assert.Exactly(t, msg.Answer, expectedMsg.Answer) }
func mustRR(s string) miekgdns.RR { r, err := miekgdns.NewRR(s) if err != nil { log.Fatal(err) } return r }
func putincache(results []*dns.Msg) { var newans []dns.RR var minttl uint32 var question dns.Question for _, r := range results { if iscachable(r) { //fmt.Printf("%s\n", r.Answer) question = r.Question[0] for _, ans := range r.Answer { //fmt.Printf("%s\n", ans.Header().Ttl) if minttl == 0 || minttl > ans.Header().Ttl { minttl = ans.Header().Ttl } if ans.Header().Rrtype == dns.TypeA { line := fmt.Sprintf("%s %d IN A %s", r.Question[0].Name, minttl, ans.(*dns.A).A) rr, _ := dns.NewRR(line) //fmt.Println(rr) newans = append(newans, rr) } } //Cache forever for now. //cache[r.Question[0]] = cacheobj{ans: r.Answer, expire: time.Now()} //fmt.Printf("Cachable for %d\n", minttl) } } fmt.Printf("Putting %d\n", len(newans)) if len(newans) > 0 { cacheput <- putrequest{question: question, ttl: minttl, result: newans} } }
func setupSOA(Zone *Zone) { label := Zone.Labels[""] primaryNs := "ns" if record, ok := label.Records[dns.TypeNS]; ok { primaryNs = record[0].RR.(*dns.RR_NS).Ns } s := Zone.Origin + ". 3600 IN SOA " + primaryNs + " support.bitnames.com. " + strconv.Itoa(Zone.Options.Serial) + " 5400 5400 2419200 " + strconv.Itoa(Zone.Options.Ttl) log.Println("SOA: ", s) rr, err := dns.NewRR(s) if err != nil { log.Println("SOA Error", err) panic("Could not setup SOA") } record := Record{RR: rr} label.Records[dns.TypeSOA] = make([]Record, 1) label.Records[dns.TypeSOA][0] = record }
// answerQuestion returns resource record answers for the domain in question func answerQuestion(name string, qtype uint16) []dns.RR { answers := make([]dns.RR, 0) // get the resource (check memory, cache, and (todo:) upstream) r, err := shaman.GetRecord(name) if err != nil { config.Log.Trace("Failed to get records for '%s' - %v", name, err) } // validate the records and append correct type to answers[] for _, record := range r.StringSlice() { entry, err := dns.NewRR(record) if err != nil { config.Log.Debug("Failed to create RR from record - %v", err) continue } entry.Header().Name = name if entry.Header().Rrtype == qtype || qtype == dns.TypeANY { answers = append(answers, entry) } } // todo: should `shaman.GetRecord` be wildcard aware (*.domain.com) or is this ok // recursively resolve if no records found if len(answers) == 0 { name = stripSubdomain(name) if len(name) > 0 { config.Log.Trace("Checking again with '%v'", name) return answerQuestion(name, qtype) } } return answers }
func TestPrivateByteSlice(t *testing.T) { dns.PrivateHandle("ISBN", TypeISBN, NewISBN) defer dns.PrivateHandleRemove(TypeISBN) rr, err := dns.NewRR(testrecord) if err != nil { t.Fatal(err) } buf := make([]byte, 100) off, err := dns.PackRR(rr, buf, 0, nil, false) if err != nil { t.Errorf("got error packing ISBN: %v", err) } custrr := rr.(*dns.PrivateRR) if ln := custrr.Data.Len() + len(custrr.Header().Name) + 11; ln != off { t.Errorf("offset is not matching to length of Private RR: %d!=%d", off, ln) } rr1, off1, err := dns.UnpackRR(buf[:off], 0) if err != nil { t.Errorf("got error unpacking ISBN: %v", err) } if off1 != off { t.Errorf("Offset after unpacking differs: %d != %d", off1, off) } if rr1.String() != testrecord { t.Errorf("Record string representation did not match original %#v != %#v", rr1.String(), testrecord) } else { t.Log(rr1.String()) } }
func rootZone(w dns.ResponseWriter, req *dns.Msg) { m := new(dns.Msg) m.SetReply(req) rr, _ := dns.NewRR(". 0 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2016110600 1800 900 604800 86400") m.Ns = []dns.RR{rr} w.WriteMsg(m) }
// Create DNS packet with the config in line with the meta zone // paper from Vixie func metazone(w dns.ResponseWriter, req *dns.Msg, c *Config) { logPrintf("metazone command") // Only called when the class is CHAOS // PTR zone. -> get a list of zone names // Top level zone stuff -- list them if strings.ToUpper(req.Question[0].Name) == "ZONE." { m := new(dns.Msg) m.SetReply(req) for _, z := range c.Zones { ptr, _ := dns.NewRR("zone. 0 CH PTR " + z.Origin) m.Answer = append(m.Answer, ptr) } w.WriteMsg(m) return } // Top level user stuff -- list them if strings.ToUpper(req.Question[0].Name) == "USER." { } // <zone>.ZONE. formerr(w, req) return }
func ExamplePrivateHandle() { dns.PrivateHandle("APAIR", TypeAPAIR, NewAPAIR) defer dns.PrivateHandleRemove(TypeAPAIR) rr, err := dns.NewRR("miek.nl. APAIR (1.2.3.4 1.2.3.5)") if err != nil { log.Fatal("could not parse APAIR record: ", err) } fmt.Println(rr) // Output: miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5 m := new(dns.Msg) m.Id = 12345 m.SetQuestion("miek.nl.", TypeAPAIR) m.Answer = append(m.Answer, rr) fmt.Println(m) // ;; opcode: QUERY, status: NOERROR, id: 12345 // ;; flags: rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 // // ;; QUESTION SECTION: // ;miek.nl. IN APAIR // // ;; ANSWER SECTION: // miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5 }
// Publish adds a record, described in RFC XXX func Publish(r string) error { rr, err := dns.NewRR(r) if err != nil { return err } local.add <- &entry{rr} return nil }
func cacheservice(req chan cacherequest, putter chan putrequest) { cache := make(map[dns.Question]cacheobj) for { select { case request := <-req: obj, exists := cache[request.msg.Question[0]] if exists { var allips []string var best string for _, a := range obj.ans { allips = append(allips, fmt.Sprintf("%s", a.(*dns.A).A)) } //fmt.Printf("%s\n", allips) if len(allips) > 0 { c := make(chan string, 1) getbest <- getbestrequest{candidates: allips, channel: c} best = <-c fmt.Printf("Best: %s\n", best) } if best != "" { //we actually have a winner line := fmt.Sprintf("%s %d IN A %s", request.msg.Question[0].Name, 60, best) rr, _ := dns.NewRR(line) request.channel <- []dns.RR{rr} } else { //Somehow we dont have a "best" answer request.channel <- obj.ans } //Check if stale if obj.expire.Before(time.Now()) { //If a stale object was served, make fresh response. fmt.Printf("Stale\n") go runquery(request.msg, true) } } else { request.channel <- nil } case put := <-putter: //toinsert := false var results []dns.RR for _, ans := range put.result { if ans.Header().Rrtype == dns.TypeA { //fmt.Printf("%s\n", ans.(*dns.A).A) //ipchan <- fmt.Sprintf("%s", ans.(*dns.A).A) results = append(results, ans) } } if len(results) > 0 { //Only save the A records if available fmt.Printf("Inserting %s : %d\n", put.question, len(results)) //fmt.Printf("%v\n", put.result) cache[put.question] = cacheobj{ans: results, expire: time.Now().Add(time.Duration(put.ttl) * time.Second)} } } } }
func parseRecordList(args []string, zone *route53.HostedZone) []dns.RR { records := []dns.RR{} origin := fmt.Sprintf("$ORIGIN %s\n", *zone.Name) for _, text := range args { record, err := dns.NewRR(origin + text) fatalIfErr(err) records = append(records, record) } return records }
/* Function that creates RR records in array from DB (accessible only from dbRespond function) */ func buildRR(handOver []string) (answerHand []dns.RR) { for _, stringAns := range handOver { temp, err := dns.NewRR(stringAns) if err != nil { panic(err.Error()) } answerHand = append(answerHand, temp) } return }
// Deal with the zone options func configZONE(w dns.ResponseWriter, req *dns.Msg, t *dns.TXT, c *Config) error { sx := strings.Split(t.Txt[0], " ") if len(sx) == 0 { return nil } switch strings.ToUpper(sx[0]) { case "READ": if len(sx) != 3 { return nil } logPrintf("config READ %s %s\n", dns.Fqdn(sx[1]), sx[2]) if e := c.ReadZoneFile(dns.Fqdn(sx[1]), sx[2]); e != nil { logPrintf("failed to read %s: %s\n", sx[2], e.Error()) return e } logPrintf("config added: READ %s %s\n", dns.Fqdn(sx[1]), sx[2]) noerr(w, req) case "READXFR": if len(sx) != 3 { return nil } logPrintf("config READXFR %s %s\n", dns.Fqdn(sx[1]), sx[2]) if e := c.ReadZoneXfr(dns.Fqdn(sx[1]), sx[2]); e != nil { logPrintf("failed to axfr %s: %s\n", sx[2], e.Error()) return e } logPrintf("config added: READXFR %s %s\n", dns.Fqdn(sx[1]), sx[2]) noerr(w, req) case "DROP": if len(sx) != 2 { return nil } logPrintf("config DROP %s\n", dns.Fqdn(sx[1])) if e := c.DropZone(dns.Fqdn(sx[1])); e != nil { logPrintf("Failed to drop %s: %s\n", dns.Fqdn(sx[1]), e.Error()) return e } logPrintf("config dropped: DROP %s\n", dns.Fqdn(sx[1])) noerr(w, req) case "LIST": logPrintf("config LIST\n") m := new(dns.Msg) m.SetReply(req) // Add the zones to the additional section for zone, _ := range c.Zones { a, _ := dns.NewRR("ZONE. TXT \"" + zone + "\"") m.Extra = append(m.Extra, a) } m.SetTsig(userFromTsig(req), dns.HmacMD5, 300, time.Now().Unix()) w.WriteMsg(m) } return nil }
func authoritativeDNSHandler(w dns.ResponseWriter, r *dns.Msg) { m := new(dns.Msg) m.SetReply(r) a, err := dns.NewRR("example.com. 3600 IN A 127.0.0.1") if err != nil { panic(err) } m.Answer = append(m.Answer, a) authority := []string{ "example.com. 7200 IN NS ns1.isp.net.", "example.com. 7200 IN NS ns2.isp.net.", } for _, rr := range authority { a, err := dns.NewRR(rr) if err != nil { panic(err) } m.Ns = append(m.Ns, a) } additional := []string{ "ns1.isp.net. 7200 IN A 127.0.0.1", "ns1.isp.net. 7200 IN AAAA ::1", "ns2.isp.net. 7200 IN A 127.0.0.2", } for _, rr := range additional { a, err := dns.NewRR(rr) if err != nil { panic(err) } m.Extra = append(m.Extra, a) } if err := w.WriteMsg(m); err != nil { panic(err) } }
func TestPrivateText(t *testing.T) { dns.PrivateHandle("ISBN", TypeISBN, NewISBN) defer dns.PrivateHandleRemove(TypeISBN) rr, err := dns.NewRR(testrecord) if err != nil { t.Fatal(err) } if rr.String() != testrecord { t.Errorf("record string representation did not match original %#v != %#v", rr.String(), testrecord) } else { t.Log(rr.String()) } }
// Export the data as dns.RR func (r *Record) ToDns() (dns.RR, error) { rdata, err := r.RdataString() if err != nil { return nil, err } rr_string := fmt.Sprintf( "%s %d %s %s %s", r.DomainName, r.Rttl, r.Rclass, r.Rtype, rdata, ) return dns.NewRR(rr_string) }
func TestFit(t *testing.T) { m := new(dns.Msg) m.SetQuestion("miek.nl", dns.TypeA) rr, _ := dns.NewRR("www.miek.nl. IN SRV 10 10 8080 blaat.miek.nl.") for i := 0; i < 101; i++ { m.Answer = append(m.Answer, rr) } // Uncompresses length is now 4424. Try trimming this to 1927 Fit(m, 1927, true) if m.Len() > 1927 { t.Fatalf("failed to fix message, expected < %d, got %d", 1927, m.Len()) } }
func TestRFC2136ValidUpdatePacket(t *testing.T) { acme.ClearFqdnCache() dns.HandleFunc(rfc2136TestZone, serverHandlerPassBackRequest) defer dns.HandleRemove(rfc2136TestZone) server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) if err != nil { t.Fatalf("Failed to start test server: %v", err) } defer server.Shutdown() txtRR, _ := dns.NewRR(fmt.Sprintf("%s %d IN TXT %s", rfc2136TestFqdn, rfc2136TestTTL, rfc2136TestValue)) rrs := []dns.RR{txtRR} m := new(dns.Msg) m.SetUpdate(rfc2136TestZone) m.RemoveRRset(rrs) m.Insert(rrs) expectstr := m.String() expect, err := m.Pack() if err != nil { t.Fatalf("Error packing expect msg: %v", err) } provider, err := NewDNSProvider(addrstr, "", "", "") if err != nil { t.Fatalf("Expected NewDNSProvider() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", "1234d=="); err != nil { t.Errorf("Expected Present() to return no error but the error was -> %v", err) } rcvMsg := <-reqChan rcvMsg.Id = m.Id actual, err := rcvMsg.Pack() if err != nil { t.Fatalf("Error packing actual msg: %v", err) } if !bytes.Equal(actual, expect) { tmp := new(dns.Msg) if err := tmp.Unpack(actual); err != nil { t.Fatalf("Error unpacking actual msg: %v", err) } t.Errorf("Expected msg:\n%s", expectstr) t.Errorf("Actual msg:\n%v", tmp) } }
func (h ErrorHandler) recovery(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) { rec := recover() if rec == nil { return } state := middleware.State{W: w, Req: r} // Obtain source of panic // From: https://gist.github.com/swdunlop/9629168 var name, file string // function name, file name var line int var pc [16]uintptr n := runtime.Callers(3, pc[:]) for _, pc := range pc[:n] { fn := runtime.FuncForPC(pc) if fn == nil { continue } file, line = fn.FileLine(pc) name = fn.Name() if !strings.HasPrefix(name, "runtime.") { break } } // Trim file path delim := "/coredns/" pkgPathPos := strings.Index(file, delim) if pkgPathPos > -1 && len(file) > pkgPathPos+len(delim) { file = file[pkgPathPos+len(delim):] } panicMsg := fmt.Sprintf("%s [PANIC %s %s] %s:%d - %v", time.Now().Format(timeFormat), r.Question[0].Name, dns.Type(r.Question[0].Qtype), file, line, rec) if h.Debug { // Write error and stack trace to the response rather than to a log var stackBuf [4096]byte stack := stackBuf[:runtime.Stack(stackBuf[:], false)] answer := debugMsg(dns.RcodeServerFailure, r) // add stack buf in TXT, limited to 255 chars for now. txt, _ := dns.NewRR(". IN 0 TXT " + string(stack[:255])) answer.Answer = append(answer.Answer, txt) state.SizeAndDo(answer) w.WriteMsg(answer) } else { // Currently we don't use the function name, since file:line is more conventional h.Log.Printf(panicMsg) } }
// update changes the SOA record to have a new serial number to reflect changes to the repository. func (s *soa) update() { s.mux.Lock() defer s.mux.Unlock() tstamp := uint32(time.Now().Unix()) refresh := 3600 retry := refresh expire := refresh minttl := 100 soa := fmt.Sprintf("%s 1000 SOA %s %s %d %d %d %d %d", s.zone.dns(), s.self.dns(), s.self.dns(), tstamp, refresh, retry, expire, minttl) var err error if s.soa, err = dns.NewRR(soa); err != nil { log.Fatalf("invalid SOA record: %s", err) } }
// Handle produces reply for A question func (a *aHandler) Handle(msg *mdns.Msg, zone *config.Zone, question mdns.Question) (err error) { s := strings.Join( []string{ question.Name, "3600", "IN", "A", zone.A, }, " ") rr, err := mdns.NewRR(s) if err == nil { msg.Answer = append(msg.Answer, rr) } return }
func setupSOA(Zone *Zone) { label := Zone.Labels[""] primaryNs := "ns" // log.Println("LABEL", label) if label == nil { log.Println(Zone.Origin, "doesn't have any 'root' records,", "you should probably add some NS records") label = Zone.AddLabel("") } if record, ok := label.Records[dns.TypeNS]; ok { primaryNs = record[0].RR.(*dns.NS).Ns } ttl := Zone.Options.Ttl * 10 if ttl > 3600 { ttl = 3600 } if ttl == 0 { ttl = 600 } s := Zone.Origin + ". " + strconv.Itoa(ttl) + " IN SOA " + primaryNs + " " + Zone.Options.Contact + " " + strconv.Itoa(Zone.Options.Serial) + // refresh, retry, expire, minimum are all // meaningless with this implementation " 5400 5400 1209600 3600" // log.Println("SOA: ", s) rr, err := dns.NewRR(s) if err != nil { log.Println("SOA Error", err) panic("Could not setup SOA") } record := Record{RR: rr} label.Records[dns.TypeSOA] = make([]Record, 1) label.Records[dns.TypeSOA][0] = record }