func (c Chaos) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := middleware.State{W: w, Req: r} if state.QClass() != dns.ClassCHAOS || state.QType() != dns.TypeTXT { return c.Next.ServeDNS(ctx, w, r) } m := new(dns.Msg) m.SetReply(r) hdr := dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0} switch state.Name() { default: return c.Next.ServeDNS(ctx, w, r) case "authors.bind.": for a, _ := range c.Authors { m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: []string{trim(a)}}) } case "version.bind.", "version.server.": m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{trim(c.Version)}}} case "hostname.bind.", "id.server.": hostname, err := os.Hostname() if err != nil { hostname = "localhost" } m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{trim(hostname)}}} } state.SizeAndDo(m) w.WriteMsg(m) return 0, nil }
// ServeDNS implements the middleware.Handler interface. func (d Dnssec) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := middleware.State{W: w, Req: r} do := state.Do() qname := state.Name() qtype := state.QType() zone := middleware.Zones(d.zones).Matches(qname) if zone == "" { return d.Next.ServeDNS(ctx, w, r) } // Intercept queries for DNSKEY, but only if one of the zones matches the qname, otherwise we let // the query through. if qtype == dns.TypeDNSKEY { for _, z := range d.zones { if qname == z { resp := d.getDNSKEY(state, z, do) state.SizeAndDo(resp) w.WriteMsg(resp) return dns.RcodeSuccess, nil } } } drr := NewDnssecResponseWriter(w, d) return d.Next.ServeDNS(ctx, drr, r) }
func (l Logger) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := middleware.State{W: w, Req: r} for _, rule := range l.Rules { if middleware.Name(rule.NameScope).Matches(state.Name()) { responseRecorder := middleware.NewResponseRecorder(w) rcode, err := l.Next.ServeDNS(ctx, responseRecorder, r) if rcode > 0 { // There was an error up the chain, but no response has been written yet. // The error must be handled here so the log entry will record the response size. if l.ErrorFunc != nil { l.ErrorFunc(responseRecorder, r, rcode) } else { rc := middleware.RcodeToString(rcode) answer := new(dns.Msg) answer.SetRcode(r, rcode) state.SizeAndDo(answer) metrics.Report(state, metrics.Dropped, rc, answer.Len(), time.Now()) w.WriteMsg(answer) } rcode = 0 } rep := middleware.NewReplacer(r, responseRecorder, CommonLogEmptyValue) rule.Log.Println(rep.Replace(rule.Format)) return rcode, err } } return l.Next.ServeDNS(ctx, w, r) }
// ServeDNS implements the middleware.Handler interface. func (c Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := middleware.State{W: w, Req: r} qname := state.Name() qtype := state.QType() zone := middleware.Zones(c.Zones).Matches(qname) if zone == "" { return c.Next.ServeDNS(ctx, w, r) } do := state.Do() // might need more from OPT record? if i, ok := c.get(qname, qtype, do); ok { resp := i.toMsg(r) state.SizeAndDo(resp) w.WriteMsg(resp) cacheHitCount.WithLabelValues(zone).Inc() return dns.RcodeSuccess, nil } cacheMissCount.WithLabelValues(zone).Inc() crr := NewCachingResponseWriter(w, c.cache, c.cap) return c.Next.ServeDNS(ctx, crr, r) }
// NoData write a nodata response to the client. func (k Kubernetes) Err(zone string, rcode int, state middleware.State) (int, error) { m := new(dns.Msg) m.SetRcode(state.Req, rcode) m.Ns = []dns.RR{k.SOA(zone, state)} state.SizeAndDo(m) state.W.WriteMsg(m) return rcode, nil }
// DefaultErrorFunc responds to an DNS request with an error. func DefaultErrorFunc(w dns.ResponseWriter, r *dns.Msg, rcode int) { state := middleware.State{W: w, Req: r} rc := middleware.RcodeToString(rcode) answer := new(dns.Msg) answer.SetRcode(r, rcode) state.SizeAndDo(answer) metrics.Report(state, metrics.Dropped, rc, answer.Len(), time.Now()) w.WriteMsg(answer) }
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) } }
// Err write an error response to the client. func (e Etcd) Err(zone string, rcode int, state middleware.State, debug []msg.Service, err error) (int, error) { m := new(dns.Msg) m.SetRcode(state.Req, rcode) m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true m.Ns, _, _ = e.SOA(zone, state) if e.debug != "" { m.Extra = servicesToTxt(debug) txt := errorToTxt(err) if txt != nil { m.Extra = append(m.Extra, errorToTxt(err)) } } state.SizeAndDo(m) state.W.WriteMsg(m) // Return success as the rcode to signal we have written to the client. return dns.RcodeSuccess, nil }
func (d *DnssecResponseWriter) WriteMsg(res *dns.Msg) error { // By definition we should sign anything that comes back, we should still figure out for // which zone it should be. state := middleware.State{W: d.ResponseWriter, Req: res} qname := state.Name() zone := middleware.Zones(d.d.zones).Matches(qname) if zone == "" { return d.ResponseWriter.WriteMsg(res) } if state.Do() { res = d.d.Sign(state, zone, time.Now().UTC()) } state.SizeAndDo(res) return d.ResponseWriter.WriteMsg(res) }
func (s Stub) ServeDNS(ctx context.Context, w dns.ResponseWriter, req *dns.Msg) (int, error) { if hasStubEdns0(req) { log.Printf("[WARNING] Forwarding cycle detected, refusing msg: %s", req.Question[0].Name) return dns.RcodeRefused, errors.New("stub forward cycle") } req = addStubEdns0(req) proxy, ok := (*s.Etcd.Stubmap)[s.Zone] if !ok { // somebody made a mistake.. return dns.RcodeServerFailure, nil } state := middleware.State{W: w, Req: req} m, e := proxy.Forward(state) if e != nil { return dns.RcodeServerFailure, e } m.RecursionAvailable, m.Compress = true, true state.SizeAndDo(m) w.WriteMsg(m) return dns.RcodeSuccess, nil }
func (h ErrorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { defer h.recovery(ctx, w, r) rcode, err := h.Next.ServeDNS(ctx, w, r) if err != nil { state := middleware.State{W: w, Req: r} errMsg := fmt.Sprintf("%s [ERROR %d %s %s] %v", time.Now().Format(timeFormat), rcode, state.Name(), state.Type(), err) if h.Debug { // Write error to response as a txt message instead of to log answer := debugMsg(rcode, r) txt, _ := dns.NewRR(". IN 0 TXT " + errMsg) answer.Answer = append(answer.Answer, txt) state.SizeAndDo(answer) w.WriteMsg(answer) return 0, err } h.Log.Println(errMsg) } return rcode, err }
// Lookup will use name and type to forge a new message and will send that upstream. It will // set any EDNS0 options correctly so that downstream will be able to process the reply. // Lookup is not suitable for forwarding request. Ssee for that. func (p Proxy) Lookup(state middleware.State, name string, tpe uint16) (*dns.Msg, error) { req := new(dns.Msg) req.SetQuestion(name, tpe) state.SizeAndDo(req) return p.lookup(state, req) }
func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := middleware.State{W: w, Req: r} if state.QClass() != dns.ClassINET { return dns.RcodeServerFailure, fmt.Errorf("can only deal with ClassINET") } name := state.Name() if e.Debug { if debug := isDebug(name); debug != "" { e.debug = r.Question[0].Name state.Clear() state.Req.Question[0].Name = debug } } // We need to check stubzones first, because we may get a request for a zone we // are not auth. for *but* do have a stubzone forward for. If we do the stubzone // handler will handle the request. if e.Stubmap != nil && len(*e.Stubmap) > 0 { for zone := range *e.Stubmap { if middleware.Name(zone).Matches(name) { stub := Stub{Etcd: e, Zone: zone} return stub.ServeDNS(ctx, w, r) } } } zone := middleware.Zones(e.Zones).Matches(state.Name()) if zone == "" { if e.Next == nil { return dns.RcodeServerFailure, nil } return e.Next.ServeDNS(ctx, w, r) } var ( records, extra []dns.RR debug []msg.Service err error ) switch state.Type() { case "A": records, debug, err = e.A(zone, state, nil) case "AAAA": records, debug, err = e.AAAA(zone, state, nil) case "TXT": records, debug, err = e.TXT(zone, state) case "CNAME": records, debug, err = e.CNAME(zone, state) case "PTR": records, debug, err = e.PTR(zone, state) case "MX": records, extra, debug, err = e.MX(zone, state) case "SRV": records, extra, debug, err = e.SRV(zone, state) case "SOA": records, debug, err = e.SOA(zone, state) case "NS": if state.Name() == zone { records, extra, debug, err = e.NS(zone, state) break } fallthrough default: // Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN _, debug, err = e.A(zone, state, nil) } if e.debug != "" { // Substitute this name with the original when we return the request. state.Clear() state.Req.Question[0].Name = e.debug } if isEtcdNameError(err) { return e.Err(zone, dns.RcodeNameError, state, debug, err) } if err != nil { return e.Err(zone, dns.RcodeServerFailure, state, debug, err) } if len(records) == 0 { return e.Err(zone, dns.RcodeSuccess, state, debug, err) } m := new(dns.Msg) m.SetReply(r) m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true m.Answer = append(m.Answer, records...) m.Extra = append(m.Extra, extra...) if e.debug != "" { m.Extra = append(m.Extra, servicesToTxt(debug)...) } m = dedup(m) state.SizeAndDo(m) m, _ = state.Scrub(m) w.WriteMsg(m) return dns.RcodeSuccess, nil }
func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := middleware.State{W: w, Req: r} if state.QClass() != dns.ClassINET { return dns.RcodeServerFailure, errors.New("can only deal with ClassINET") } qname := state.Name() zone := middleware.Zones(f.Zones.Names).Matches(qname) if zone == "" { if f.Next != nil { return f.Next.ServeDNS(ctx, w, r) } return dns.RcodeServerFailure, errors.New("no next middleware found") } z, ok := f.Zones.Z[zone] if !ok { return f.Next.ServeDNS(ctx, w, r) } if z == nil { return dns.RcodeServerFailure, nil } if r.Opcode == dns.OpcodeNotify { if z.isNotify(state) { m := new(dns.Msg) m.SetReply(r) m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true state.SizeAndDo(m) w.WriteMsg(m) log.Printf("[INFO] Notify from %s for %s: checking transfer", state.IP(), zone) ok, err := z.shouldTransfer() if ok { z.TransferIn() } else { log.Printf("[INFO] Notify from %s for %s: no serial increase seen", state.IP(), zone) } if err != nil { log.Printf("[WARNING] Notify from %s for %s: failed primary check: %s", state.IP(), zone, err) } return dns.RcodeSuccess, nil } log.Printf("[INFO] Dropping notify from %s for %s", state.IP(), zone) return dns.RcodeSuccess, nil } if z.Expired != nil && *z.Expired { log.Printf("[ERROR] Zone %s is expired", zone) return dns.RcodeServerFailure, nil } if state.QType() == dns.TypeAXFR || state.QType() == dns.TypeIXFR { xfr := Xfr{z} return xfr.ServeDNS(ctx, w, r) } answer, ns, extra, result := z.Lookup(qname, state.QType(), state.Do()) m := new(dns.Msg) m.SetReply(r) m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true m.Answer, m.Ns, m.Extra = answer, ns, extra switch result { case Success: case NoData: case NameError: m.Rcode = dns.RcodeNameError case Delegation: m.Authoritative = false case ServerFailure: return dns.RcodeServerFailure, nil } state.SizeAndDo(m) m, _ = state.Scrub(m) w.WriteMsg(m) return dns.RcodeSuccess, nil }
func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { log.Printf("[debug] here entering ServeDNS: ctx:%v dnsmsg:%v\n", ctx, r) state := middleware.State{W: w, Req: r} if state.QClass() != dns.ClassINET { return dns.RcodeServerFailure, fmt.Errorf("can only deal with ClassINET") } // Check that query matches one of the zones served by this middleware, // otherwise delegate to the next in the pipeline. zone := middleware.Zones(k.Zones).Matches(state.Name()) if zone == "" { if k.Next == nil { return dns.RcodeServerFailure, nil } return k.Next.ServeDNS(ctx, w, r) } m := new(dns.Msg) m.SetReply(r) m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true var ( records, extra []dns.RR err error ) switch state.Type() { case "A": records, err = k.A(zone, state, nil) case "AAAA": records, err = k.AAAA(zone, state, nil) case "TXT": records, err = k.TXT(zone, state) // TODO: change lookup to return appropriate error. Then add code below // this switch to check for the error and return not implemented. //return dns.RcodeNotImplemented, nil case "CNAME": records, err = k.CNAME(zone, state) case "MX": records, extra, err = k.MX(zone, state) case "SRV": records, extra, err = k.SRV(zone, state) case "SOA": records = []dns.RR{k.SOA(zone, state)} case "NS": if state.Name() == zone { records, extra, err = k.NS(zone, state) break } fallthrough default: // Do a fake A lookup, so we can distinguish betwen NODATA and NXDOMAIN _, err = k.A(zone, state, nil) } if isKubernetesNameError(err) { return k.Err(zone, dns.RcodeNameError, state) } if err != nil { return dns.RcodeServerFailure, err } if len(records) == 0 { return k.Err(zone, dns.RcodeSuccess, state) } m.Answer = append(m.Answer, records...) m.Extra = append(m.Extra, extra...) m = dedup(m) state.SizeAndDo(m) m, _ = state.Scrub(m) w.WriteMsg(m) return dns.RcodeSuccess, nil }