func (s *server) signSet(r []dns.RR, now time.Time, incep, expir uint32) (*dns.RRSIG, error) { key := cache.Key(r) if m, exp, hit := s.scache.Search(key); hit { // There can only be one sig in this cache. // Is it still valid 24 hours from now? if now.Add(+24*time.Hour).Sub(exp) < -24*time.Hour { return m.Answer[0].(*dns.RRSIG), nil } s.scache.Remove(key) } logf("scache miss for %s type %d", r[0].Header().Name, r[0].Header().Rrtype) StatsDnssecCacheMiss.Inc(1) promCacheMiss.WithLabelValues("signature").Inc() sig, err, shared := inflight.Do(key, func() (*dns.RRSIG, error) { sig1 := s.NewRRSIG(incep, expir) sig1.Header().Ttl = r[0].Header().Ttl if r[0].Header().Rrtype == dns.TypeTXT { sig1.OrigTtl = 0 } e := sig1.Sign(s.config.PrivKey, r) if e != nil { logf("failed to sign: %s", e.Error()) } return sig1, e }) if err != nil { return nil, err } if !shared { s.scache.InsertSignature(key, sig) } return dns.Copy(sig).(*dns.RRSIG), nil }
func TestCacheTruncated(t *testing.T) { s := newTestServer(t, true) m := &dns.Msg{} m.SetQuestion("skydns.test.", dns.TypeSRV) m.Truncated = true s.rcache.InsertMessage(cache.Key(m.Question[0], false, false), m) // Now asking for this should result in a non-truncated answer. resp, _ := dns.Exchange(m, "127.0.0.1:"+StrPort) if resp.Truncated { t.Fatal("truncated bit should be false") } }
// ServeDNS is the handler for DNS requests, responsible for parsing DNS request, possibly forwarding // it to a real dns server and returning a response. func (s *server) ServeDNS(w dns.ResponseWriter, req *dns.Msg) { m := new(dns.Msg) m.SetReply(req) m.Authoritative = true m.RecursionAvailable = true m.Compress = true bufsize := uint16(512) dnssec := false tcp := false start := time.Now() q := req.Question[0] name := strings.ToLower(q.Name) if q.Qtype == dns.TypeANY { m.Authoritative = false m.Rcode = dns.RcodeRefused m.RecursionAvailable = false m.RecursionDesired = false m.Compress = false // if write fails don't care w.WriteMsg(m) promErrorCount.WithLabelValues("refused").Inc() return } if o := req.IsEdns0(); o != nil { bufsize = o.UDPSize() dnssec = o.Do() } if bufsize < 512 { bufsize = 512 } // with TCP we can send 64K if tcp = isTCP(w); tcp { bufsize = dns.MaxMsgSize - 1 promRequestCount.WithLabelValues("tcp").Inc() } else { promRequestCount.WithLabelValues("udp").Inc() } StatsRequestCount.Inc(1) if dnssec { StatsDnssecOkCount.Inc(1) promDnssecOkCount.Inc() } if s.config.Verbose { logf("received DNS Request for %q from %q with type %d", q.Name, w.RemoteAddr(), q.Qtype) } // Check cache first. m1 := s.rcache.Hit(q, dnssec, tcp, m.Id) if m1 != nil { if tcp { if _, overflow := Fit(m1, dns.MaxMsgSize, tcp); overflow { promErrorCount.WithLabelValues("overflow").Inc() msgFail := new(dns.Msg) s.ServerFailure(msgFail, req) w.WriteMsg(msgFail) return } } else { // Overflow with udp always results in TC. Fit(m1, int(bufsize), tcp) if m1.Truncated { promErrorCount.WithLabelValues("truncated").Inc() } } // Still round-robin even with hits from the cache. // Only shuffle A and AAAA records with each other. if q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA { s.RoundRobin(m1.Answer) } if err := w.WriteMsg(m1); err != nil { logf("failure to return reply %q", err) } metricSizeAndDuration(m1, start, tcp) return } for zone, ns := range *s.config.stub { if strings.HasSuffix(name, zone) { resp := s.ServeDNSStubForward(w, req, ns) if resp != nil { s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp) metricSizeAndDuration(resp, start, tcp) } return } } // If the qname is local.ds.skydns.local. and s.config.Local != "", substitute that name. if s.config.Local != "" && name == s.config.localDomain { name = s.config.Local } if q.Qtype == dns.TypePTR && strings.HasSuffix(name, ".in-addr.arpa.") || strings.HasSuffix(name, ".ip6.arpa.") { resp := s.ServeDNSReverse(w, req) if resp != nil { s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp) metricSizeAndDuration(resp, start, tcp) } return } if q.Qclass != dns.ClassCHAOS && !strings.HasSuffix(name, s.config.Domain) { resp := s.ServeDNSForward(w, req) if resp != nil { s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp) metricSizeAndDuration(resp, start, tcp) } return } promCacheMiss.WithLabelValues("response").Inc() defer func() { if m.Rcode == dns.RcodeServerFailure { if err := w.WriteMsg(m); err != nil { logf("failure to return reply %q", err) } return } // Set TTL to the minimum of the RRset and dedup the message, i.e. remove identical RRs. m = s.dedup(m) minttl := s.config.Ttl if len(m.Answer) > 1 { for _, r := range m.Answer { if r.Header().Ttl < minttl { minttl = r.Header().Ttl } } for _, r := range m.Answer { r.Header().Ttl = minttl } } if dnssec { if s.config.PubKey != nil { m.AuthenticatedData = true s.Denial(m) s.Sign(m, bufsize) } } if tcp { if _, overflow := Fit(m, dns.MaxMsgSize, tcp); overflow { msgFail := new(dns.Msg) s.ServerFailure(msgFail, req) w.WriteMsg(msgFail) return } } else { Fit(m, int(bufsize), tcp) if m.Truncated { promErrorCount.WithLabelValues("truncated").Inc() } } s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), m) if err := w.WriteMsg(m); err != nil { logf("failure to return reply %q", err) } metricSizeAndDuration(m, start, tcp) }() if name == s.config.Domain { if q.Qtype == dns.TypeSOA { m.Answer = []dns.RR{s.NewSOA()} return } if q.Qtype == dns.TypeDNSKEY { if s.config.PubKey != nil { m.Answer = []dns.RR{s.config.PubKey} return } } } if q.Qclass == dns.ClassCHAOS { if q.Qtype == dns.TypeTXT { switch name { case "authors.bind.": fallthrough case s.config.Domain: hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0} authors := []string{"Erik St. Martin", "Brian Ketelsen", "Miek Gieben", "Michael Crosby"} for _, a := range authors { m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: []string{a}}) } for j := 0; j < len(authors)*(int(dns.Id())%4+1); j++ { q := int(dns.Id()) % len(authors) p := int(dns.Id()) % len(authors) if q == p { p = (p + 1) % len(authors) } m.Answer[q], m.Answer[p] = m.Answer[p], m.Answer[q] } return case "version.bind.": fallthrough case "version.server.": hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0} m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{Version}}} return case "hostname.bind.": fallthrough case "id.server.": // TODO(miek): machine name to return hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0} m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{"localhost"}}} return } } // still here, fail m.SetReply(req) m.SetRcode(req, dns.RcodeServerFailure) return } switch q.Qtype { case dns.TypeNS: if name != s.config.Domain { break } // Lookup s.config.DnsDomain records, extra, err := s.NSRecords(q, s.config.dnsDomain) if isEtcdNameError(err, s) { s.NameError(m, req) return } m.Answer = append(m.Answer, records...) m.Extra = append(m.Extra, extra...) case dns.TypeA, dns.TypeAAAA: records, err := s.AddressRecords(q, name, nil, bufsize, dnssec, false) if isEtcdNameError(err, s) { s.NameError(m, req) return } m.Answer = append(m.Answer, records...) case dns.TypeTXT: records, err := s.TXTRecords(q, name) if isEtcdNameError(err, s) { s.NameError(m, req) return } m.Answer = append(m.Answer, records...) case dns.TypeCNAME: records, err := s.CNAMERecords(q, name) if isEtcdNameError(err, s) { s.NameError(m, req) return } m.Answer = append(m.Answer, records...) case dns.TypeMX: records, extra, err := s.MXRecords(q, name, bufsize, dnssec) if isEtcdNameError(err, s) { s.NameError(m, req) return } m.Answer = append(m.Answer, records...) m.Extra = append(m.Extra, extra...) default: fallthrough // also catch other types, so that they return NODATA case dns.TypeSRV: records, extra, err := s.SRVRecords(q, name, bufsize, dnssec) if err != nil { if isEtcdNameError(err, s) { s.NameError(m, req) return } logf("got error from backend: %s", err) if q.Qtype == dns.TypeSRV { // Otherwise NODATA s.ServerFailure(m, req) return } } // if we are here again, check the types, because an answer may only // be given for SRV. All other types should return NODATA, the // NXDOMAIN part is handled in the above code. TODO(miek): yes this // can be done in a more elegant manor. if q.Qtype == dns.TypeSRV { m.Answer = append(m.Answer, records...) m.Extra = append(m.Extra, extra...) } } if len(m.Answer) == 0 { // NODATA response StatsNoDataCount.Inc(1) m.Ns = []dns.RR{s.NewSOA()} m.Ns[0].Header().Ttl = s.config.MinTtl } }