Ejemplo n.º 1
0
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)
}
Ejemplo n.º 2
0
func (e Etcd) AAAA(zone string, state middleware.State, previousRecords []dns.RR) (records []dns.RR, debug []msg.Service, err error) {
	services, debug, err := e.records(state, false)
	if err != nil {
		return nil, debug, err
	}

	for _, serv := range services {
		ip := net.ParseIP(serv.Host)
		switch {
		case ip == nil:
			// Try to resolve as CNAME if it's not an IP, but only if we don't create loops.
			if middleware.Name(state.Name()).Matches(dns.Fqdn(serv.Host)) {
				// x CNAME x is a direct loop, don't add those
				continue
			}

			newRecord := serv.NewCNAME(state.QName(), serv.Host)
			if len(previousRecords) > 7 {
				// don't add it, and just continue
				continue
			}
			if isDuplicateCNAME(newRecord, previousRecords) {
				continue
			}

			state1 := copyState(state, serv.Host, state.QType())
			nextRecords, nextDebug, err := e.AAAA(zone, state1, append(previousRecords, newRecord))

			if err == nil {
				// Not only have we found something we should add the CNAME and the IP addresses.
				if len(nextRecords) > 0 {
					records = append(records, newRecord)
					records = append(records, nextRecords...)
					debug = append(debug, nextDebug...)
				}
				continue
			}
			// This means we can not complete the CNAME, try to look else where.
			target := newRecord.Target
			if dns.IsSubDomain(zone, target) {
				// We should already have found it
				continue
			}
			m1, e1 := e.Proxy.Lookup(state, target, state.QType())
			if e1 != nil {
				continue
			}
			// Len(m1.Answer) > 0 here is well?
			records = append(records, newRecord)
			records = append(records, m1.Answer...)
			continue
			// both here again
		case ip.To4() != nil:
			// nada?
		case ip.To4() == nil:
			records = append(records, serv.NewAAAA(state.QName(), ip.To16()))
		}
	}
	return records, debug, nil
}
Ejemplo n.º 3
0
// 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
}
Ejemplo n.º 4
0
func (k Kubernetes) A(zone string, state middleware.State, previousRecords []dns.RR) (records []dns.RR, err error) {
	services, err := k.records(state, false)
	if err != nil {
		return nil, err
	}

	for _, serv := range services {
		ip := net.ParseIP(serv.Host)
		switch {
		case ip == nil:
			// TODO(miek): lowercasing? Should lowercase in everything see #85
			if middleware.Name(state.Name()).Matches(dns.Fqdn(serv.Host)) {
				// x CNAME x is a direct loop, don't add those
				continue
			}

			newRecord := serv.NewCNAME(state.QName(), serv.Host)
			if len(previousRecords) > 7 {
				// don't add it, and just continue
				continue
			}
			if isDuplicateCNAME(newRecord, previousRecords) {
				continue
			}

			state1 := copyState(state, serv.Host, state.QType())
			nextRecords, err := k.A(zone, state1, append(previousRecords, newRecord))

			if err == nil {
				// Not only have we found something we should add the CNAME and the IP addresses.
				if len(nextRecords) > 0 {
					records = append(records, newRecord)
					records = append(records, nextRecords...)
				}
				continue
			}
			// This means we can not complete the CNAME, try to look else where.
			target := newRecord.Target
			if dns.IsSubDomain(zone, target) {
				// We should already have found it
				continue
			}
			m1, e1 := k.Proxy.Lookup(state, target, state.QType())
			if e1 != nil {
				continue
			}
			// Len(m1.Answer) > 0 here is well?
			records = append(records, newRecord)
			records = append(records, m1.Answer...)
			continue
		case ip.To4() != nil:
			records = append(records, serv.NewA(state.QName(), ip.To4()))
		case ip.To4() == nil:
			// nodata?
		}
	}
	return records, nil
}
Ejemplo n.º 5
0
func (k Kubernetes) records(state middleware.State, exact bool) ([]msg.Service, error) {
	services, err := k.Records(state.Name(), exact)
	if err != nil {
		return nil, err
	}
	// TODO: Do we want to support the SkyDNS (hacky) Group feature?
	services = msg.Group(services)
	return services, nil
}
Ejemplo n.º 6
0
// 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)
}
Ejemplo n.º 7
0
// 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)
}
Ejemplo n.º 8
0
// 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)
}
Ejemplo n.º 9
0
func (e Etcd) records(state middleware.State, exact bool) (services, debug []msg.Service, err error) {
	services, err = e.Records(state.Name(), exact)
	if err != nil {
		return
	}
	if e.debug != "" {
		debug = services
	}
	services = msg.Group(services)
	return
}
Ejemplo n.º 10
0
// PTR returns the PTR records, only services that have a domain name as host are included.
func (e Etcd) PTR(zone string, state middleware.State) (records []dns.RR, debug []msg.Service, err error) {
	services, debug, err := e.records(state, true)
	if err != nil {
		return nil, debug, err
	}

	for _, serv := range services {
		if ip := net.ParseIP(serv.Host); ip == nil {
			records = append(records, serv.NewPTR(state.QName(), serv.Host))
		}
	}
	return records, debug, nil
}
Ejemplo n.º 11
0
func (e Etcd) CNAME(zone string, state middleware.State) (records []dns.RR, debug []msg.Service, err error) {
	services, debug, err := e.records(state, true)
	if err != nil {
		return nil, debug, err
	}

	if len(services) > 0 {
		serv := services[0]
		if ip := net.ParseIP(serv.Host); ip == nil {
			records = append(records, serv.NewCNAME(state.QName(), serv.Host))
		}
	}
	return records, debug, nil
}
Ejemplo n.º 12
0
func (e Etcd) TXT(zone string, state middleware.State) (records []dns.RR, debug []msg.Service, err error) {
	services, debug, err := e.records(state, false)
	if err != nil {
		return nil, debug, err
	}

	for _, serv := range services {
		if serv.Text == "" {
			continue
		}
		records = append(records, serv.NewTXT(state.QName()))
	}
	return records, debug, nil
}
Ejemplo n.º 13
0
func (e Etcd) NS(zone string, state middleware.State) (records, extra []dns.RR, debug []msg.Service, err error) {
	// NS record for this zone live in a special place, ns.dns.<zone>. Fake our lookup.
	// only a tad bit fishy...
	old := state.QName()

	state.Clear()
	state.Req.Question[0].Name = "ns.dns." + zone
	services, debug, err := e.records(state, false)
	if err != nil {
		return nil, nil, debug, err
	}
	// ... and reset
	state.Req.Question[0].Name = old

	for _, serv := range services {
		ip := net.ParseIP(serv.Host)
		switch {
		case ip == nil:
			return nil, nil, debug, fmt.Errorf("NS record must be an IP address: %s", serv.Host)
		case ip.To4() != nil:
			serv.Host = msg.Domain(serv.Key)
			records = append(records, serv.NewNS(state.QName()))
			extra = append(extra, serv.NewA(serv.Host, ip.To4()))
		case ip.To4() == nil:
			serv.Host = msg.Domain(serv.Key)
			records = append(records, serv.NewNS(state.QName()))
			extra = append(extra, serv.NewAAAA(serv.Host, ip.To16()))
		}
	}
	return records, extra, debug, nil
}
Ejemplo n.º 14
0
// isNotify checks if state is a notify message and if so, will *also* check if it
// is from one of the configured masters. If not it will not be a valid notify
// message. If the zone z is not a secondary zone the message will also be ignored.
func (z *Zone) isNotify(state middleware.State) bool {
	if state.Req.Opcode != dns.OpcodeNotify {
		return false
	}
	if len(z.TransferFrom) == 0 {
		return false
	}
	remote := middleware.Addr(state.IP()).Normalize()
	for _, from := range z.TransferFrom {
		if from == remote {
			return true
		}
	}
	return false
}
Ejemplo n.º 15
0
// Sign signs the message m. it takes care of negative or nodata responses. It
// uses NSEC black lies for authenticated denial of existence. Signatures
// creates will be cached for a short while. By default we sign for 8 days,
// starting 3 hours ago.
func (d Dnssec) Sign(state middleware.State, zone string, now time.Time) *dns.Msg {
	req := state.Req
	mt, _ := middleware.Classify(req) // TODO(miek): need opt record here?
	if mt == middleware.Delegation {
		return req
	}

	incep, expir := incepExpir(now)

	if mt == middleware.NameError {
		if req.Ns[0].Header().Rrtype != dns.TypeSOA || len(req.Ns) > 1 {
			return req
		}

		ttl := req.Ns[0].Header().Ttl

		if sigs, err := d.sign(req.Ns, zone, ttl, incep, expir); err == nil {
			req.Ns = append(req.Ns, sigs...)
		}
		if sigs, err := d.nsec(state.Name(), zone, ttl, incep, expir); err == nil {
			req.Ns = append(req.Ns, sigs...)
		}
		if len(req.Ns) > 1 { // actually added nsec and sigs, reset the rcode
			req.Rcode = dns.RcodeSuccess
		}
		return req
	}

	for _, r := range rrSets(req.Answer) {
		ttl := r[0].Header().Ttl
		if sigs, err := d.sign(r, zone, ttl, incep, expir); err == nil {
			req.Answer = append(req.Answer, sigs...)
		}
	}
	for _, r := range rrSets(req.Ns) {
		ttl := r[0].Header().Ttl
		if sigs, err := d.sign(r, zone, ttl, incep, expir); err == nil {
			req.Ns = append(req.Ns, sigs...)
		}
	}
	for _, r := range rrSets(req.Extra) {
		ttl := r[0].Header().Ttl
		if sigs, err := d.sign(r, zone, ttl, incep, expir); err == nil {
			req.Extra = append(sigs, req.Extra...) // prepend to leave OPT alone
		}
	}
	return req
}
Ejemplo n.º 16
0
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)
	}
}
Ejemplo n.º 17
0
func (m Metrics) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
	state := middleware.State{W: w, Req: r}

	qname := state.QName()
	zone := middleware.Zones(m.ZoneNames).Matches(qname)
	if zone == "" {
		zone = "."
	}

	// Record response to get status code and size of the reply.
	rw := middleware.NewResponseRecorder(w)
	status, err := m.Next.ServeDNS(ctx, rw, r)

	Report(state, zone, rw.Rcode(), rw.Size(), rw.Start())

	return status, err
}
Ejemplo n.º 18
0
// 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
}
Ejemplo n.º 19
0
func (p Proxy) lookup(state middleware.State, r *dns.Msg) (*dns.Msg, error) {
	var (
		reply *dns.Msg
		err   error
	)
	for _, upstream := range p.Upstreams {
		// allowed bla bla bla TODO(miek): fix full proxy spec from caddy?
		start := time.Now()

		// Since Select() should give us "up" hosts, keep retrying
		// hosts until timeout (or until we get a nil host).
		for time.Now().Sub(start) < tryDuration {
			host := upstream.Select()

			if host == nil {
				return nil, errUnreachable
			}

			atomic.AddInt64(&host.Conns, 1)
			// tls+tcp ?
			if state.Proto() == "tcp" {
				reply, err = middleware.Exchange(p.Client.TCP, r, host.Name)
			} else {
				reply, err = middleware.Exchange(p.Client.UDP, r, host.Name)
			}
			atomic.AddInt64(&host.Conns, -1)

			if err == nil {
				return reply, nil
			}
			timeout := host.FailTimeout
			if timeout == 0 {
				timeout = 10 * time.Second
			}
			atomic.AddInt32(&host.Fails, 1)
			go func(host *UpstreamHost, timeout time.Duration) {
				time.Sleep(timeout)
				atomic.AddInt32(&host.Fails, -1)
			}(host, timeout)
		}
		return nil, errUnreachable
	}
	return nil, errUnreachable
}
Ejemplo n.º 20
0
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
}
Ejemplo n.º 21
0
// MX returns MX records from etcd.
// If the Target is not a name but an IP address, a name is created on the fly.
func (e Etcd) MX(zone string, state middleware.State) (records, extra []dns.RR, debug []msg.Service, err error) {
	services, debug, err := e.records(state, false)
	if err != nil {
		return nil, nil, debug, err
	}

	lookup := make(map[string]bool)
	for _, serv := range services {
		if !serv.Mail {
			continue
		}
		ip := net.ParseIP(serv.Host)
		switch {
		case ip == nil:
			mx := serv.NewMX(state.QName())
			records = append(records, mx)
			if _, ok := lookup[mx.Mx]; ok {
				break
			}

			lookup[mx.Mx] = true

			if !dns.IsSubDomain(zone, mx.Mx) {
				m1, e1 := e.Proxy.Lookup(state, mx.Mx, dns.TypeA)
				if e1 == nil {
					extra = append(extra, m1.Answer...)
				}
				m1, e1 = e.Proxy.Lookup(state, mx.Mx, dns.TypeAAAA)
				if e1 == nil {
					// If we have seen CNAME's we *assume* that they are already added.
					for _, a := range m1.Answer {
						if _, ok := a.(*dns.CNAME); !ok {
							extra = append(extra, a)
						}
					}
				}
				break
			}
			// Internal name
			state1 := copyState(state, mx.Mx, dns.TypeA)
			addr, debugAddr, e1 := e.A(zone, state1, nil)
			if e1 == nil {
				extra = append(extra, addr...)
				debug = append(debug, debugAddr...)
			}
			// e.AAAA as well
		case ip.To4() != nil:
			serv.Host = msg.Domain(serv.Key)
			records = append(records, serv.NewMX(state.QName()))
			extra = append(extra, serv.NewA(serv.Host, ip.To4()))
		case ip.To4() == nil:
			serv.Host = msg.Domain(serv.Key)
			records = append(records, serv.NewMX(state.QName()))
			extra = append(extra, serv.NewAAAA(serv.Host, ip.To16()))
		}
	}
	return records, extra, debug, nil
}
Ejemplo n.º 22
0
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
}
Ejemplo n.º 23
0
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)
}
Ejemplo n.º 24
0
// Serve an AXFR (and fallback of IXFR) as well.
func (x Xfr) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
	state := middleware.State{W: w, Req: r}
	if !x.TransferAllowed(state) {
		return dns.RcodeServerFailure, nil
	}
	if state.QType() != dns.TypeAXFR && state.QType() != dns.TypeIXFR {
		return 0, fmt.Errorf("xfr called with non transfer type: %d", state.QType())
	}

	records := x.All()
	if len(records) == 0 {
		return dns.RcodeServerFailure, nil
	}

	ch := make(chan *dns.Envelope)
	defer close(ch)
	tr := new(dns.Transfer)
	go tr.Out(w, r, ch)

	j, l := 0, 0
	records = append(records, records[0]) // add closing SOA to the end
	log.Printf("[INFO] Outgoing transfer of %d records of zone %s to %s started", len(records), x.origin, state.IP())
	for i, r := range records {
		l += dns.Len(r)
		if l > transferLength {
			ch <- &dns.Envelope{RR: records[j:i]}
			l = 0
			j = i
		}
	}
	if j < len(records) {
		ch <- &dns.Envelope{RR: records[j:]}
	}

	w.Hijack()
	// w.Close() // Client closes connection
	return dns.RcodeSuccess, nil
}
Ejemplo n.º 25
0
// Report is a plain reporting function that the server can use for REFUSED and other
// queries that are turned down because they don't match any middleware.
func Report(state middleware.State, zone, rcode string, size int, start time.Time) {
	if requestCount == nil {
		// no metrics are enabled
		return
	}

	// Proto and Family
	net := state.Proto()
	fam := "1"
	if state.Family() == 2 {
		fam = "2"
	}

	typ := state.QType()

	requestCount.WithLabelValues(zone, net, fam).Inc()
	requestDuration.WithLabelValues(zone).Observe(float64(time.Since(start) / time.Millisecond))

	if state.Do() {
		requestDo.WithLabelValues(zone).Inc()
	}

	if _, known := monitorType[typ]; known {
		requestType.WithLabelValues(zone, dns.Type(typ).String()).Inc()
	} else {
		requestType.WithLabelValues(zone, other).Inc()
	}

	if typ == dns.TypeIXFR || typ == dns.TypeAXFR {
		responseTransferSize.WithLabelValues(zone, net).Observe(float64(size))
		requestTransferSize.WithLabelValues(zone, net).Observe(float64(state.Size()))
	} else {
		responseSize.WithLabelValues(zone, net).Observe(float64(size))
		requestSize.WithLabelValues(zone, net).Observe(float64(state.Size()))
	}

	responseRcode.WithLabelValues(zone, rcode).Inc()
}
Ejemplo n.º 26
0
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
}
Ejemplo n.º 27
0
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
}
Ejemplo n.º 28
0
// 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)
}
Ejemplo n.º 29
0
// SRV returns SRV records from etcd.
// If the Target is not a name but an IP address, a name is created on the fly.
func (e Etcd) SRV(zone string, state middleware.State) (records, extra []dns.RR, debug []msg.Service, err error) {
	services, debug, err := e.records(state, false)
	if err != nil {
		return nil, nil, nil, err
	}

	// Looping twice to get the right weight vs priority
	w := make(map[int]int)
	for _, serv := range services {
		weight := 100
		if serv.Weight != 0 {
			weight = serv.Weight
		}
		if _, ok := w[serv.Priority]; !ok {
			w[serv.Priority] = weight
			continue
		}
		w[serv.Priority] += weight
	}
	lookup := make(map[string]bool)
	for _, serv := range services {
		w1 := 100.0 / float64(w[serv.Priority])
		if serv.Weight == 0 {
			w1 *= 100
		} else {
			w1 *= float64(serv.Weight)
		}
		weight := uint16(math.Floor(w1))
		ip := net.ParseIP(serv.Host)
		switch {
		case ip == nil:
			srv := serv.NewSRV(state.QName(), weight)
			records = append(records, srv)

			if _, ok := lookup[srv.Target]; ok {
				break
			}

			lookup[srv.Target] = true

			if !dns.IsSubDomain(zone, srv.Target) {
				m1, e1 := e.Proxy.Lookup(state, srv.Target, dns.TypeA)
				if e1 == nil {
					extra = append(extra, m1.Answer...)
				}
				m1, e1 = e.Proxy.Lookup(state, srv.Target, dns.TypeAAAA)
				if e1 == nil {
					// If we have seen CNAME's we *assume* that they are already added.
					for _, a := range m1.Answer {
						if _, ok := a.(*dns.CNAME); !ok {
							extra = append(extra, a)
						}
					}
				}
				break
			}
			// Internal name, we should have some info on them, either v4 or v6
			// Clients expect a complete answer, because we are a recursor in their view.
			state1 := copyState(state, srv.Target, dns.TypeA)
			addr, debugAddr, e1 := e.A(zone, state1, nil)
			if e1 == nil {
				extra = append(extra, addr...)
				debug = append(debug, debugAddr...)
			}
			// e.AAA(zone, state1, nil) as well...?
		case ip.To4() != nil:
			serv.Host = msg.Domain(serv.Key)
			srv := serv.NewSRV(state.QName(), weight)

			records = append(records, srv)
			extra = append(extra, serv.NewA(srv.Target, ip.To4()))
		case ip.To4() == nil:
			serv.Host = msg.Domain(serv.Key)
			srv := serv.NewSRV(state.QName(), weight)

			records = append(records, srv)
			extra = append(extra, serv.NewAAAA(srv.Target, ip.To16()))
		}
	}
	return records, extra, debug, nil
}
Ejemplo n.º 30
0
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
}