Example #1
0
func (res *Resolver) handleEmpty(rs *records.RecordGenerator, name string, m, r *dns.Msg) error {
	qType := r.Question[0].Qtype
	switch qType {
	case dns.TypeSOA, dns.TypeNS, dns.TypeSRV:
		logging.CurLog.MesosSuccess.Inc()
		return nil
	}

	m.Rcode = dns.RcodeNameError
	if qType == dns.TypeAAAA && len(rs.SRVs[name])+len(rs.As[name]) > 0 {
		m.Rcode = dns.RcodeSuccess
	}

	logging.CurLog.MesosNXDomain.Inc()
	logging.VeryVerbose.Println("total A rrs:\t" + strconv.Itoa(len(rs.As)))
	logging.VeryVerbose.Println("failed looking for " + r.Question[0].String())

	rr, err := res.formatSOA(r.Question[0].Name)
	if err != nil {
		return err
	}

	m.Ns = append(m.Ns, rr)
	return nil
}
Example #2
0
func (h *dnsHandler) leaderResponse(name string, q dns.Question, res *dns.Msg) {
	if q.Qtype != dns.TypeA {
		return
	}

	zone := strings.Replace(name, "leader.", "", 1)
	if zone != h.zone {
		servers, err := h.store.getServers(zone)
		if err != nil {
			res.Rcode = dns.RcodeServerFailure
			return
		}
		if len(servers) == 0 {
			res.Rcode = dns.RcodeNameError
			return
		}

		referral(res, fmt.Sprintf("%s.%s", zone, h.domain), servers)
		return
	}

	leader, err := h.store.getLeader()
	if err != nil {
		res.Rcode = dns.RcodeServerFailure
		return
	}
	res.Answer = append(res.Answer, newRR(q, leader))
}
Example #3
0
// handlerFunc receives requests, looks up the result and returns what is found.
func handlerFunc(res dns.ResponseWriter, req *dns.Msg) {
	message := new(dns.Msg)
	switch req.Opcode {
	case dns.OpcodeQuery:
		message.SetReply(req)
		message.Compress = false
		message.Answer = make([]dns.RR, 0)

		for _, question := range message.Question {
			answers := answerQuestion(strings.ToLower(question.Name), question.Qtype)
			if len(answers) > 0 {
				for i := range answers {
					message.Answer = append(message.Answer, answers[i])
				}
			} else {
				// If there are no records, go back through and search for SOA records
				for _, question := range message.Question {
					answers := answerQuestion(strings.ToLower(question.Name), dns.TypeSOA)
					for i := range answers {
						message.Ns = append(message.Ns, answers[i])
					}
				}
			}
		}
		if len(message.Answer) == 0 && len(message.Ns) == 0 {
			message.Rcode = dns.RcodeNameError
		}
	default:
		message = message.SetRcode(req, dns.RcodeNotImplemented)
	}
	res.WriteMsg(message)
}
Example #4
0
File: dns.go Project: grkvlt/weave
func makeDNSFailResponse(r *dns.Msg) *dns.Msg {
	m := new(dns.Msg)
	m.SetReply(r)
	m.RecursionAvailable = true
	m.Rcode = dns.RcodeNameError
	return m
}
Example #5
0
func (t *UsageTracker) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
	var latest *usage.ProjectVersion
	var event *TrackingEvent

	m := new(dns.Msg)
	m.SetReply(r)

	q := r.Question[0].Name

	pv, err := usage.ParseV1(q)
	if err != nil {
		log.Printf("error parsing %s (%s): %s", q, w.RemoteAddr().(*net.UDPAddr).IP, err)
		// m.Rcode = dns.RcodeRefused
		m.Rcode = dns.RcodeNameError
		goto response
	}

	latest, err = t.GetLatest(pv)
	if err != nil {
		// TODO if format is right, but project is missing,
		// return an NXDOMAIN error
		log.Printf("error fetching latest for %v: %s", pv, err)
		m.Rcode = dns.RcodeNameError
		goto response
	}

	// do this after getting the version so we don't track results for
	// projects that aren't found
	event = &TrackingEvent{*pv, ""}
	if addr, ok := w.RemoteAddr().(*net.UDPAddr); ok {
		event.ClientAddress = addr.IP.String()
	}
	if err = t.Track(event); err != nil {
		log.Printf("error tracking %v: %s", event, err)
		// tracking error is not fatal, so still return the results
	}

	m.Answer = append(m.Answer, PtrRecord(latest))

response:
	err = w.WriteMsg(m)
	if err != nil {
		log.Printf("error writing response %v: %s", m, err)
	}
}
Example #6
0
func prepareFailureMsg(req *dns.Msg) *dns.Msg {
	failMsg := new(dns.Msg)
	failMsg.Id = req.Id
	failMsg.Response = true
	failMsg.Authoritative = true
	failMsg.Question = req.Question
	failMsg.Rcode = dns.RcodeNameError
	return failMsg
}
Example #7
0
func prepareAnswerMsg(req *dns.Msg, answers []dns.RR) *dns.Msg {
	answerMsg := new(dns.Msg)
	answerMsg.Id = req.Id
	answerMsg.Response = true
	answerMsg.Authoritative = true
	answerMsg.Question = req.Question
	answerMsg.Answer = answers
	answerMsg.Rcode = dns.RcodeSuccess
	answerMsg.Extra = []dns.RR{}
	return answerMsg
}
Example #8
0
func (d *DNSServer) errorResponse(r *dns.Msg, code int, w dns.ResponseWriter) {
	m := dns.Msg{}
	m.SetReply(r)
	m.RecursionAvailable = true
	m.Rcode = code

	d.ns.debugf("error response: %+v", m)
	if err := w.WriteMsg(&m); err != nil {
		d.ns.infof("error responding: %v", err)
	}
}
Example #9
0
func (h *dnsHandler) serviceResponse(name string, q dns.Question, res *dns.Msg) {
	if q.Qtype != dns.TypeA && q.Qtype != dns.TypeSRV {
		return
	}

	all := strings.Count(name, ".") == 5 && strings.HasPrefix(name, allPrefix)
	if all {
		name = name[len(allPrefix):]
	}

	srv, err := infoFromAddr(name)
	if err != nil {
		res.Rcode = dns.RcodeNameError
		return
	}

	var instances instances
	if all {
		instances, err = h.store.getAllInstances(srv)
	} else {
		instances, err = h.store.getHealthyInstances(srv)
	}
	if err != nil {
		// TODO(ts): Maybe return NoError for registered service without
		//           instances.
		if isNoInstances(err) {
			res.Rcode = dns.RcodeNameError
			return
		}

		res.Rcode = dns.RcodeServerFailure
		return
	}

	for _, i := range instances {
		res.Answer = append(res.Answer, newRR(q, i))
	}
}
Example #10
0
func (h *dnsHandler) serverResponse(name string, q dns.Question, res *dns.Msg) {
	if q.Qtype != dns.TypeA && q.Qtype != dns.TypeNS {
		return
	}

	ns, zone := parseServerQuestion(name)
	if ns != "" && q.Qtype == dns.TypeNS {
		return
	}

	servers, err := h.store.getServers(zone)
	if err != nil && !isNoInstances(err) {
		res.Rcode = dns.RcodeServerFailure
		return
	}
	sort.Sort(servers)

	// return list of all nameservers
	if ns == "" {
		for i, server := range servers {
			server.host = fmt.Sprintf("ns%d.%s", i, q.Name)
			res.Answer = append(res.Answer, newRR(q, server))
		}
		return
	}

	// return requested nameserver
	index, err := strconv.Atoi(ns[2:])
	if err != nil {
		res.Rcode = dns.RcodeNameError
		return
	}
	if len(servers) <= index {
		return
	}
	res.Answer = append(res.Answer, newRR(q, servers[index]))
}
Example #11
0
func (res *Resolver) handleEmpty(rs *records.RecordGenerator, name string, m, r *dns.Msg) error {
	qType := r.Question[0].Qtype
	switch qType {
	case dns.TypeSOA, dns.TypeNS, dns.TypeSRV:
		logging.CurLog.MesosSuccess.Inc()
		return nil
	}

	m.Rcode = dns.RcodeNameError

	// Because we don't implement AAAA records, AAAA queries will always
	// go via this path
	// Unfortunately, we don't implement AAAA queries in Mesos-DNS,
	// and although the 'Not Implemented' error code seems more suitable,
	// RFCs do not recommend it: https://tools.ietf.org/html/rfc4074
	// Therefore we always return success, which is synonymous with NODATA
	// to get a positive cache on no records AAAA
	// Further information:
	// PR: https://github.com/mesosphere/mesos-dns/pull/366
	// Issue: https://github.com/mesosphere/mesos-dns/issues/363

	// The second component is just a matter of returning NODATA if we have
	// SRV or A records for the given name, but no neccessarily the given query

	if (qType == dns.TypeAAAA) || (len(rs.SRVs[name])+len(rs.As[name]) > 0) {
		m.Rcode = dns.RcodeSuccess
	}

	logging.CurLog.MesosNXDomain.Inc()
	logging.VeryVerbose.Println("total A rrs:\t" + strconv.Itoa(len(rs.As)))
	logging.VeryVerbose.Println("failed looking for " + r.Question[0].String())

	m.Ns = append(m.Ns, res.formatSOA(r.Question[0].Name))

	return nil
}
Example #12
0
func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
	q := r.Question[0]

	info := fmt.Sprintf("Question: Type=%s Class=%s Name=%s", dns.TypeToString[q.Qtype], dns.ClassToString[q.Qclass], q.Name)

	if q.Qtype == dns.TypeA && q.Qclass == dns.ClassINET {
		m := new(dns.Msg)
		m.SetReply(r)
		a := new(dns.A)
		a.Hdr = dns.RR_Header{Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 600}
		a.A = resolveIP
		m.Answer = []dns.RR{a}
		w.WriteMsg(m)
		log.Printf("%s (RESOLVED)\n", info)
	} else {
		m := new(dns.Msg)
		m.SetReply(r)
		m.Rcode = dns.RcodeNameError // NXDOMAIN
		w.WriteMsg(m)
		log.Printf("%s (NXDOMAIN)\n", info)
	}
}
Example #13
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
}
Example #14
0
// 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()

	if req.Question[0].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()
	}

	defer func() {
		promCacheSize.WithLabelValues("response").Set(float64(s.rcache.Size()))
	}()

	// Check cache first.
	key := cache.QuestionKey(req.Question[0], dnssec)
	m1, exp, hit := s.rcache.Search(key)
	if hit {
		// Cache hit! \o/
		if time.Since(exp) < 0 {
			m1.Id = m.Id
			m1.Compress = true
			m1.Truncated = false

			if dnssec {
				// The key for DNS/DNSSEC in cache is different, no
				// need to do Denial/Sign here.
				//if s.config.PubKey != nil {
				//s.Denial(m1) // not needed for cache hits
				//s.Sign(m1, bufsize)
				//}
			}
			if m1.Len() > int(bufsize) && !tcp {
				promErrorCount.WithLabelValues("truncated").Inc()
				m1.Truncated = true
			}
			// Still round-robin even with hits from the cache.
			// Only shuffle A and AAAA records with each other.
			if req.Question[0].Qtype == dns.TypeA || req.Question[0].Qtype == dns.TypeAAAA {
				s.RoundRobin(m1.Answer)
			}

			if err := w.WriteMsg(m1); err != nil {
				log.Printf("skydns: failure to return reply %q", err)
			}
			metricSizeAndDuration(m1, start, tcp)
			return
		}
		// Expired! /o\
		s.rcache.Remove(key)
	}

	q := req.Question[0]
	name := strings.ToLower(q.Name)

	if s.config.Verbose {
		log.Printf("skydns: received DNS Request for %q from %q with type %d", q.Name, w.RemoteAddr(), q.Qtype)
	}

	for zone, ns := range *s.config.stub {
		if strings.HasSuffix(name, zone) {
			resp := s.ServeDNSStubForward(w, req, ns)
			metricSizeAndDuration(resp, start, tcp)
			return
		}
	}

	// If the qname is local.dns.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)
		metricSizeAndDuration(resp, start, tcp)
		return
	}

	if q.Qclass != dns.ClassCHAOS && !strings.HasSuffix(name, s.config.Domain) {
		if s.config.Verbose {
			log.Printf("skydns: %q is not sub of %q, forwarding...", name, s.config.Domain)
		}

		resp := s.ServeDNSForward(w, req)
		metricSizeAndDuration(resp, start, tcp)
		return
	}

	promCacheMiss.WithLabelValues("response").Inc()

	defer func() {
		if m.Rcode == dns.RcodeServerFailure {
			if err := w.WriteMsg(m); err != nil {
				log.Printf("skydns: 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 !m.Truncated {
			s.rcache.InsertMessage(cache.QuestionKey(req.Question[0], dnssec), m)
		}

		if dnssec {
			if s.config.PubKey != nil {
				m.AuthenticatedData = true
				s.Denial(m)
				s.Sign(m, bufsize)
			}
		}

		if m.Len() > dns.MaxMsgSize {
			log.Printf("skydns: overflowing maximum message size: %d, dropping additional section", m.Len())
			m.Extra = nil // Drop entire additional section to see if this helps.

			if m.Len() > dns.MaxMsgSize {
				// *Still* too large.
				log.Printf("skydns: still overflowing maximum message size: %d", m.Len())
				promErrorCount.WithLabelValues("overflow").Inc()
				m1 := new(dns.Msg) // Use smaller msg to signal failure.
				m1.SetRcode(m, dns.RcodeServerFailure)
				if err := w.WriteMsg(m1); err != nil {
					log.Printf("skydns: failure to return reply %q", err)
				}
				metricSizeAndDuration(m1, start, tcp)
				return
			}
		}

		if m.Len() > int(bufsize) && !tcp {
			m.Extra = nil // As above, drop entire additional section.
			if m.Len() > int(bufsize) {
				promErrorCount.WithLabelValues("truncated").Inc()
				m.Truncated = true
			}
		}

		if err := w.WriteMsg(m); err != nil {
			log.Printf("skydns: failure to return reply %q %d", err, m.Len())
		}
		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 {
			log.Printf("skydns: %q unmatch default domain", name)
			break
		}
		// Lookup s.config.DnsDomain
		records, extra, err := s.NSRecords(q, s.config.dnsDomain)
		if err != nil {
			if e, ok := err.(*etcd.EtcdError); ok {
				if e.ErrorCode == 100 {
					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 err != nil {
			if e, ok := err.(*etcd.EtcdError); ok {
				if e.ErrorCode == 100 {
					s.NameError(m, req)
					return
				}
			}
		}
		m.Answer = append(m.Answer, records...)
	case dns.TypeTXT:
		records, err := s.TXTRecords(q, name)
		if err != nil {
			if e, ok := err.(*etcd.EtcdError); ok {
				if e.ErrorCode == 100 {
					s.NameError(m, req)
					return
				}
			}
		}
		m.Answer = append(m.Answer, records...)
	case dns.TypeCNAME:
		records, err := s.CNAMERecords(q, name)
		if err != nil {
			if e, ok := err.(*etcd.EtcdError); ok {
				if e.ErrorCode == 100 {
					s.NameError(m, req)
					return
				}
			}
		}
		m.Answer = append(m.Answer, records...)
	case dns.TypeMX:
		records, extra, err := s.MXRecords(q, name, bufsize, dnssec)
		if err != nil {
			if e, ok := err.(*etcd.EtcdError); ok {
				if e.ErrorCode == 100 {
					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 e, ok := err.(*etcd.EtcdError); ok {
				if e.ErrorCode == 100 {
					s.NameError(m, req)
					return
				}
			}
			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
	}
}
Example #15
0
func TestUDPDNSServer(t *testing.T) {
	setupForTest(t)

	const (
		successTestName = "test1.weave.local."
		failTestName    = "fail.weave.local."
		nonLocalName    = "weave.works."
		testAddr1       = "10.2.2.1"
		containerID     = "somecontainer"
	)
	testCIDR1 := testAddr1 + "/24"

	InitDefaultLogging(testing.Verbose())
	Info.Println("TestUDPDNSServer starting")

	zone, err := NewZoneDb(ZoneConfig{})
	require.NoError(t, err)
	err = zone.Start()
	require.NoError(t, err)
	defer zone.Stop()

	ip, _, _ := net.ParseCIDR(testCIDR1)
	zone.AddRecord(containerID, successTestName, ip)

	fallbackHandler := func(w dns.ResponseWriter, req *dns.Msg) {
		if len(req.Question) == 0 {
			return // ignore empty queries (sent when shutting down the server)
		}
		m := new(dns.Msg)
		m.SetReply(req)
		q := req.Question[0]
		if q.Name == nonLocalName && q.Qtype == dns.TypeMX {
			m.Answer = make([]dns.RR, 1)
			m.Answer[0] = &dns.MX{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 0}, Mx: "mail." + nonLocalName}
		} else if q.Name == nonLocalName && q.Qtype == dns.TypeANY {
			m.Answer = make([]dns.RR, 512/len("mailn."+nonLocalName)+1)
			for i := range m.Answer {
				m.Answer[i] = &dns.MX{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 0}, Mx: fmt.Sprintf("mail%d.%s", i, nonLocalName)}
			}
		} else if q.Name == testRDNSnonlocal && q.Qtype == dns.TypePTR {
			m.Answer = make([]dns.RR, 1)
			m.Answer[0] = &dns.PTR{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: 0}, Ptr: "ns1.google.com."}
		} else if q.Name == testRDNSfail && q.Qtype == dns.TypePTR {
			m.Rcode = dns.RcodeNameError
		}
		w.WriteMsg(m)
	}

	// Run another DNS server for fallback
	fallback, err := newMockedFallback(fallbackHandler, nil)
	require.NoError(t, err)
	fallback.Start()
	defer fallback.Stop()

	srv, err := NewDNSServer(DNSServerConfig{
		Zone:              zone,
		UpstreamCfg:       fallback.CliConfig,
		CacheDisabled:     true,
		ListenReadTimeout: testSocketTimeout,
	})
	require.NoError(t, err)
	err = srv.Start()
	require.NoError(t, err)
	go srv.ActivateAndServe()
	defer srv.Stop()
	time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start

	var r *dns.Msg

	testPort, err := srv.GetPort()
	require.NoError(t, err)
	require.NotEqual(t, 0, testPort, "invalid listen port")

	_, r = assertExchange(t, successTestName, dns.TypeA, testPort, 1, 1, 0)
	require.IsType(t, (*dns.A)(nil), r.Answer[0], "DNS record")
	require.Equal(t, testAddr1, r.Answer[0].(*dns.A).A.String(), "IP address")

	assertExchange(t, failTestName, dns.TypeA, testPort, 0, 0, dns.RcodeNameError)

	_, r = assertExchange(t, testRDNSsuccess, dns.TypePTR, testPort, 1, 1, 0)
	require.IsType(t, (*dns.PTR)(nil), r.Answer[0], "DNS record")
	require.Equal(t, successTestName, r.Answer[0].(*dns.PTR).Ptr, "IP address")

	assertExchange(t, testRDNSfail, dns.TypePTR, testPort, 0, 0, dns.RcodeNameError)

	// This should fail because we don't handle MX records
	assertExchange(t, successTestName, dns.TypeMX, testPort, 0, 0, dns.RcodeNameError)

	// This non-local query for an MX record should succeed by being
	// passed on to the fallback server
	assertExchange(t, nonLocalName, dns.TypeMX, testPort, 1, -1, 0)

	// Now ask a query that we expect to return a lot of data.
	assertExchange(t, nonLocalName, dns.TypeANY, testPort, 5, -1, 0)

	assertExchange(t, testRDNSnonlocal, dns.TypePTR, testPort, 1, -1, 0)

	// Not testing MDNS functionality of server here (yet), since it
	// needs two servers, each listening on its own address
}
Example #16
0
func mockDNSQuery(w dns.ResponseWriter, r *dns.Msg) {
	m := new(dns.Msg)
	m.SetReply(r)
	m.Compress = false

	appendAnswer := func(rr dns.RR) {
		m.Answer = append(m.Answer, rr)
	}
	for _, q := range r.Question {
		q.Name = strings.ToLower(q.Name)
		if q.Name == "servfail.com." {
			m.Rcode = dns.RcodeServerFailure
			break
		}
		switch q.Qtype {
		case dns.TypeSOA:
			record := new(dns.SOA)
			record.Hdr = dns.RR_Header{Name: "letsencrypt.org.", Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: 0}
			record.Ns = "ns.letsencrypt.org."
			record.Mbox = "master.letsencrypt.org."
			record.Serial = 1
			record.Refresh = 1
			record.Retry = 1
			record.Expire = 1
			record.Minttl = 1
			appendAnswer(record)
		case dns.TypeAAAA:
			if q.Name == "v6.letsencrypt.org." {
				record := new(dns.AAAA)
				record.Hdr = dns.RR_Header{Name: "v6.letsencrypt.org.", Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0}
				record.AAAA = net.ParseIP("::1")
				appendAnswer(record)
			}
			if q.Name == "dualstack.letsencrypt.org." {
				record := new(dns.AAAA)
				record.Hdr = dns.RR_Header{Name: "dualstack.letsencrypt.org.", Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0}
				record.AAAA = net.ParseIP("::1")
				appendAnswer(record)
			}
			if q.Name == "v4error.letsencrypt.org." {
				record := new(dns.AAAA)
				record.Hdr = dns.RR_Header{Name: "v4error.letsencrypt.org.", Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0}
				record.AAAA = net.ParseIP("::1")
				appendAnswer(record)
			}
			if q.Name == "v6error.letsencrypt.org." {
				m.SetRcode(r, dns.RcodeNotImplemented)
			}
			if q.Name == "nxdomain.letsencrypt.org." {
				m.SetRcode(r, dns.RcodeNameError)
			}
			if q.Name == "dualstackerror.letsencrypt.org." {
				m.SetRcode(r, dns.RcodeNotImplemented)
			}
		case dns.TypeA:
			if q.Name == "cps.letsencrypt.org." {
				record := new(dns.A)
				record.Hdr = dns.RR_Header{Name: "cps.letsencrypt.org.", Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}
				record.A = net.ParseIP("127.0.0.1")
				appendAnswer(record)
			}
			if q.Name == "dualstack.letsencrypt.org." {
				record := new(dns.A)
				record.Hdr = dns.RR_Header{Name: "dualstack.letsencrypt.org.", Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}
				record.A = net.ParseIP("127.0.0.1")
				appendAnswer(record)
			}
			if q.Name == "v6error.letsencrypt.org." {
				record := new(dns.A)
				record.Hdr = dns.RR_Header{Name: "dualstack.letsencrypt.org.", Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}
				record.A = net.ParseIP("127.0.0.1")
				appendAnswer(record)
			}
			if q.Name == "v4error.letsencrypt.org." {
				m.SetRcode(r, dns.RcodeNotImplemented)
			}
			if q.Name == "nxdomain.letsencrypt.org." {
				m.SetRcode(r, dns.RcodeNameError)
			}
			if q.Name == "dualstackerror.letsencrypt.org." {
				m.SetRcode(r, dns.RcodeRefused)
			}
		case dns.TypeCNAME:
			if q.Name == "cname.letsencrypt.org." {
				record := new(dns.CNAME)
				record.Hdr = dns.RR_Header{Name: "cname.letsencrypt.org.", Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: 30}
				record.Target = "cps.letsencrypt.org."
				appendAnswer(record)
			}
			if q.Name == "cname.example.com." {
				record := new(dns.CNAME)
				record.Hdr = dns.RR_Header{Name: "cname.example.com.", Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: 30}
				record.Target = "CAA.example.com."
				appendAnswer(record)
			}
		case dns.TypeDNAME:
			if q.Name == "dname.letsencrypt.org." {
				record := new(dns.DNAME)
				record.Hdr = dns.RR_Header{Name: "dname.letsencrypt.org.", Rrtype: dns.TypeDNAME, Class: dns.ClassINET, Ttl: 30}
				record.Target = "cps.letsencrypt.org."
				appendAnswer(record)
			}
		case dns.TypeCAA:
			if q.Name == "bracewel.net." || q.Name == "caa.example.com." {
				record := new(dns.CAA)
				record.Hdr = dns.RR_Header{Name: q.Name, Rrtype: dns.TypeCAA, Class: dns.ClassINET, Ttl: 0}
				record.Tag = "issue"
				record.Value = "letsencrypt.org"
				record.Flag = 1
				appendAnswer(record)
			}
			if q.Name == "cname.example.com." {
				record := new(dns.CAA)
				record.Hdr = dns.RR_Header{Name: "caa.example.com.", Rrtype: dns.TypeCAA, Class: dns.ClassINET, Ttl: 0}
				record.Tag = "issue"
				record.Value = "letsencrypt.org"
				record.Flag = 1
				appendAnswer(record)
			}
		case dns.TypeTXT:
			if q.Name == "split-txt.letsencrypt.org." {
				record := new(dns.TXT)
				record.Hdr = dns.RR_Header{Name: "split-txt.letsencrypt.org.", Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}
				record.Txt = []string{"a", "b", "c"}
				appendAnswer(record)
			} else {
				auth := new(dns.SOA)
				auth.Hdr = dns.RR_Header{Name: "letsencrypt.org.", Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: 0}
				auth.Ns = "ns.letsencrypt.org."
				auth.Mbox = "master.letsencrypt.org."
				auth.Serial = 1
				auth.Refresh = 1
				auth.Retry = 1
				auth.Expire = 1
				auth.Minttl = 1
				m.Ns = append(m.Ns, auth)
			}
			if q.Name == "nxdomain.letsencrypt.org." {
				m.SetRcode(r, dns.RcodeNameError)
			}
		}
	}

	err := w.WriteMsg(m)
	if err != nil {
		panic(err) // running tests, so panic is OK
	}
	return
}
Example #17
0
func route(w dns.ResponseWriter, req *dns.Msg) {
	clientIp, _, _ := net.SplitHostPort(w.RemoteAddr().String())

	// One question at a time please
	if len(req.Question) != 1 {
		dns.HandleFailed(w, req)
		log.WithFields(log.Fields{"client": clientIp}).Warn("Rejected multi-question query")
		return
	}

	question := req.Question[0]
	rrString := dns.Type(question.Qtype).String()

	// We are assuming the JSON config has all names as lower case
	fqdn := strings.ToLower(question.Name)

	// Internets only
	if question.Qclass != dns.ClassINET {
		m := new(dns.Msg)
		m.SetReply(req)
		m.Authoritative = false
		m.RecursionDesired = false
		m.RecursionAvailable = false
		m.Rcode = dns.RcodeNotImplemented
		w.WriteMsg(m)
		log.WithFields(log.Fields{"question": fqdn, "type": rrString, "client": clientIp}).Warn("Rejected non-inet query")
		return
	}

	// ANY queries are bad, mmmkay...
	if question.Qtype == dns.TypeANY {
		m := new(dns.Msg)
		m.SetReply(req)
		m.Authoritative = false
		m.RecursionDesired = false
		m.RecursionAvailable = false
		m.Rcode = dns.RcodeNotImplemented
		w.WriteMsg(m)
		log.WithFields(log.Fields{"question": fqdn, "type": rrString, "client": clientIp}).Warn("Rejected ANY query")
		return
	}

	proto := "UDP"
	if isTcp(w) {
		proto = "TCP"
	}

	log.WithFields(log.Fields{"question": fqdn, "type": rrString, "client": clientIp, "proto": proto}).Debug("Request")

	// A records may return CNAME answer(s) plus A answer(s)
	if question.Qtype == dns.TypeA {
		found, ok := answers.Addresses(clientIp, fqdn, nil, 1)
		if ok && len(found) > 0 {
			log.WithFields(log.Fields{"client": clientIp, "type": rrString, "question": fqdn, "answers": len(found)}).Info("Answered locally")
			Respond(w, req, found)
			return
		}
	} else {
		// Specific request for another kind of record
		keys := []string{clientIp, DEFAULT_KEY}
		for _, key := range keys {
			// Client-specific answers
			found, ok := answers.Matching(question.Qtype, key, fqdn)
			if ok {
				log.WithFields(log.Fields{"client": key, "type": rrString, "question": fqdn, "answers": len(found)}).Info("Answered from config for ", key)
				Respond(w, req, found)
				return
			}
		}

		log.Debug("No match found in config")
	}

	// Phone a friend
	msg, err := ResolveTryAll(fqdn, question.Qtype, answers.Recursers(clientIp))
	if err == nil {
		msg.SetReply(req)
		w.WriteMsg(msg)
		log.WithFields(log.Fields{"client": clientIp, "type": rrString, "question": fqdn}).Info("Sent recursive response")
		return
	}

	// I give up
	log.WithFields(log.Fields{"client": clientIp, "type": rrString, "question": fqdn}).Warn("No answer found")
	dns.HandleFailed(w, req)
}
Example #18
0
// 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

	if req.Question[0].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)
		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 _, ok := w.RemoteAddr().(*net.TCPAddr); ok {
		bufsize = dns.MaxMsgSize - 1
		tcp = true
	}
	// Check cache first.
	key := cache.QuestionKey(req.Question[0], dnssec)
	m1, exp, hit := s.rcache.Search(key)
	if hit {
		// Cache hit! \o/
		if time.Since(exp) < 0 {
			m1.Id = m.Id
			if dnssec {
				StatsDnssecOkCount.Inc(1)
				// The key for DNS/DNSSEC in cache is different, no
				// need to do Denial/Sign here.
				//if s.config.PubKey != nil {
				//s.Denial(m1) // not needed for cache hits
				//s.Sign(m1, bufsize)
				//}
			}
			if m1.Len() > int(bufsize) && !tcp {
				m1.Truncated = true
			}
			// Still round-robin even with hits from the cache.
			if s.config.RoundRobin && (req.Question[0].Qtype == dns.TypeA ||
				req.Question[0].Qtype == dns.TypeAAAA) {
				switch l := len(m1.Answer); l {
				case 2:
					if dns.Id()%2 == 0 {
						m1.Answer[0], m1.Answer[1] = m1.Answer[1], m1.Answer[0]
					}
				default:
					// Do a minimum of l swap, maximum of 4l swaps
					for j := 0; j < l*(int(dns.Id())%4+1); j++ {
						q := int(dns.Id()) % l
						p := int(dns.Id()) % l
						if q == p {
							p = (p + 1) % l
						}
						m1.Answer[q], m1.Answer[p] = m1.Answer[p], m1.Answer[q]
					}
				}

			}
			if err := w.WriteMsg(m1); err != nil {
				s.config.log.Errorf("failure to return reply %q", err)
			}
			return
		}
		// Expired! /o\
		s.rcache.Remove(key)
	}

	q := req.Question[0]
	name := strings.ToLower(q.Name)
	StatsRequestCount.Inc(1)
	if verbose {
		s.config.log.Infof("received DNS Request for %q from %q with type %d", q.Name, w.RemoteAddr(), q.Qtype)
	}
	// If the qname is local.dns.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.") {
		s.ServeDNSReverse(w, req)
		return
	}

	if q.Qclass != dns.ClassCHAOS && !strings.HasSuffix(name, s.config.Domain) {
		s.ServeDNSForward(w, req)
		return
	}

	defer func() {
		if m.Rcode == dns.RcodeServerFailure {
			if err := w.WriteMsg(m); err != nil {
				s.config.log.Errorf("failure to return reply %q", err)
			}
			return
		}
		// Set TTL to the minimum of the RRset.
		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
			}
		}

		s.rcache.InsertMessage(cache.QuestionKey(req.Question[0], dnssec), m)

		if dnssec {
			StatsDnssecOkCount.Inc(1)
			if s.config.PubKey != nil {
				m.AuthenticatedData = true
				s.Denial(m)
				s.Sign(m, bufsize)
			}
		}
		if m.Len() > int(bufsize) && !tcp {
			// TODO(miek): this is a little brain dead, better is to not add
			// RRs in the message in the first place.
			m.Truncated = true
		}
		if err := w.WriteMsg(m); err != nil {
			s.config.log.Errorf("failure to return reply %q", err)
		}
	}()

	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 err != nil {
			if e, ok := err.(*etcd.EtcdError); ok {
				if e.ErrorCode == 100 {
					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)
		if err != nil {
			if e, ok := err.(*etcd.EtcdError); ok {
				if e.ErrorCode == 100 {
					s.NameError(m, req)
					return
				}
			}
			if err.Error() == "incomplete CNAME chain" {
				// We can not complete the CNAME internally, *iff* there is a
				// external name in the set, take it, and try to resolve it externally.
				if len(records) == 0 {
					s.NameError(m, req)
					return
				}
				target := ""
				for _, r := range records {
					if v, ok := r.(*dns.CNAME); ok {
						if !dns.IsSubDomain(s.config.Domain, v.Target) {
							target = v.Target
							break
						}
					}
				}
				if target == "" {
					s.config.log.Warningf("incomplete CNAME chain for %s", name)
					s.NoDataError(m, req)
					return
				}
				m1, e1 := s.Lookup(target, req.Question[0].Qtype, bufsize, dnssec)
				if e1 != nil {
					s.config.log.Errorf("%q", err)
					s.NoDataError(m, req)
					return
				}
				records = append(records, m1.Answer...)
			}
		}
		m.Answer = append(m.Answer, records...)
	case dns.TypeCNAME:
		records, err := s.CNAMERecords(q, name)
		if err != nil {
			if e, ok := err.(*etcd.EtcdError); ok {
				if e.ErrorCode == 100 {
					s.NameError(m, req)
					return
				}
			}
		}
		m.Answer = append(m.Answer, records...)
	default:
		fallthrough // also catch other types, so that they return NODATA
	case dns.TypeSRV, dns.TypeANY:
		records, extra, err := s.SRVRecords(q, name, bufsize, dnssec)
		if err != nil {
			if e, ok := err.(*etcd.EtcdError); ok {
				if e.ErrorCode == 100 {
					s.NameError(m, req)
					return
				}
			}
		}
		// if we are here again, check the types, because an answer may only
		// be given for SRV or ANY. 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 || q.Qtype == dns.TypeANY {
			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
	}
}
Example #19
0
func route(w dns.ResponseWriter, req *dns.Msg) {
	// Setup reply
	m := new(dns.Msg)
	m.SetReply(req)
	m.Authoritative = true
	m.RecursionAvailable = true
	m.Compress = true

	clientIp, _, _ := net.SplitHostPort(w.RemoteAddr().String())

	// One question at a time please
	if len(req.Question) != 1 {
		dns.HandleFailed(w, req)
		log.WithFields(log.Fields{"client": clientIp}).Warn("Rejected multi-question query")
		return
	}

	question := req.Question[0]
	rrString := dns.Type(question.Qtype).String()

	// We are assuming the config has all names as lower case
	fqdn := strings.ToLower(question.Name)

	// Internets only
	if question.Qclass != dns.ClassINET {
		m.Authoritative = false
		m.RecursionDesired = false
		m.RecursionAvailable = false
		m.Rcode = dns.RcodeNotImplemented
		w.WriteMsg(m)
		log.WithFields(log.Fields{"question": fqdn, "type": rrString, "client": clientIp}).Warn("Rejected non-inet query")
		return
	}

	// ANY queries are bad, mmmkay...
	if question.Qtype == dns.TypeANY {
		m.Authoritative = false
		m.RecursionDesired = false
		m.RecursionAvailable = false
		m.Rcode = dns.RcodeNotImplemented
		w.WriteMsg(m)
		log.WithFields(log.Fields{"question": fqdn, "type": rrString, "client": clientIp}).Warn("Rejected ANY query")
		return
	}

	proto := "UDP"
	if isTcp(w) {
		proto = "TCP"
	}

	log.WithFields(log.Fields{"question": fqdn, "type": rrString, "client": clientIp, "proto": proto}).Debug("Request")

	if msg := clientSpecificCacheHit(clientIp, req); msg != nil {
		Respond(w, req, msg)
		log.WithFields(log.Fields{"client": clientIp, "type": rrString, "question": fqdn}).Debug("Sent client-specific cached response")
		return
	}

	if msg := globalCacheHit(req); msg != nil {
		Respond(w, req, msg)
		log.WithFields(log.Fields{"client": clientIp, "type": rrString, "question": fqdn}).Debug("Sent globally cached response")
		return
	}

	// A records may return CNAME answer(s) plus A answer(s)
	if question.Qtype == dns.TypeA {
		found, ok := answers.Addresses(clientIp, fqdn, nil, 1)
		if ok && len(found) > 0 {
			log.WithFields(log.Fields{"client": clientIp, "type": rrString, "question": fqdn, "answers": len(found)}).Debug("Answered locally")
			m.Answer = found
			addToClientSpecificCache(clientIp, req, m)
			Respond(w, req, m)
			return
		}
	} else {
		// Specific request for another kind of record
		keys := []string{clientIp, DEFAULT_KEY}
		for _, key := range keys {
			// Client-specific answers
			found, ok := answers.Matching(question.Qtype, key, fqdn)
			if ok {
				log.WithFields(log.Fields{"client": key, "type": rrString, "question": fqdn, "answers": len(found)}).Debug("Answered from config for ", key)
				m.Answer = found
				addToClientSpecificCache(clientIp, req, m)
				Respond(w, req, m)
				return
			}
		}

		log.Debug("No match found in config")
	}

	// If we are authoritative for a suffix the label has, there's no point trying the recursive DNS
	authoritativeFor := answers.AuthoritativeSuffixes()
	for _, suffix := range authoritativeFor {
		if strings.HasSuffix(fqdn, suffix) {
			log.WithFields(log.Fields{"client": clientIp, "type": rrString, "question": fqdn}).Debugf("Not answered locally, but I am authoritative for %s", suffix)
			m.Authoritative = true
			m.RecursionAvailable = false
			me := strings.TrimLeft(suffix, ".")
			hdr := dns.RR_Header{Name: me, Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: uint32(*defaultTtl)}
			serial++
			record := &dns.SOA{Hdr: hdr, Ns: me, Mbox: me, Serial: serial, Refresh: 60, Retry: 10, Expire: 86400, Minttl: 1}
			m.Ns = append(m.Ns, record)
			Respond(w, req, m)
			return
		}
	}

	// Phone a friend - Forward original query
	msg, err := ResolveTryAll(req, answers.Recursers(clientIp))
	if err == nil {
		msg.Compress = true
		msg.Id = req.Id

		// We don't support AAAA, but an NXDOMAIN from the recursive resolver
		// doesn't necessarily mean there are never any records for that domain,
		// so rewrite the response code to NOERROR.
		if (question.Qtype == dns.TypeAAAA) && (msg.Rcode == dns.RcodeNameError) {
			log.WithFields(log.Fields{"client": clientIp, "type": rrString, "question": fqdn}).Debug("Rewrote AAAA NXDOMAIN to NOERROR")
			msg.Rcode = dns.RcodeSuccess
		}

		addToGlobalCache(req, msg)

		Respond(w, req, msg)
		log.WithFields(log.Fields{"client": clientIp, "type": rrString, "question": fqdn}).Debug("Sent recursive response")
		return
	}

	// I give up
	log.WithFields(log.Fields{"client": clientIp, "type": rrString, "question": fqdn}).Info("No answer found")
	dns.HandleFailed(w, req)
}
Example #20
0
// Check that AddRecord/DeleteRecord/... in the Zone database lead to cache invalidations
func TestServerDbCacheInvalidation(t *testing.T) {
	const (
		containerID = "somecontainer"
		testName1   = "first.weave.local."
		testName2   = "second.weave.local."
	)

	EnableDebugLogging(testing.Verbose())
	Log.Infoln("TestServerDbCacheInvalidation starting")

	clk := newMockedClock()

	Log.Debugf("Creating mocked mDNS client and server")
	mdnsServer1 := newMockedMDNSServerWithRecord(Record{testName1, net.ParseIP("10.2.2.9"), 0, 0, 0})
	mdnsCli1 := newMockedMDNSClient([]*mockedMDNSServer{mdnsServer1})

	Log.Debugf("Creating zone database with the mocked mDNS client and server")
	zoneConfig := ZoneConfig{
		MDNSServer: mdnsServer1,
		MDNSClient: mdnsCli1,
		Clock:      clk,
	}
	zone, err := NewZoneDb(zoneConfig)
	require.NoError(t, err)
	err = zone.Start()
	require.NoError(t, err)
	defer zone.Stop()

	Log.Debugf("Creating a cache")
	cache, err := NewCache(1024, clk)
	require.NoError(t, err)

	fallbackHandler := func(w dns.ResponseWriter, req *dns.Msg) {
		m := new(dns.Msg)
		m.SetReply(req)
		if len(req.Question) == 1 {
			m.Rcode = dns.RcodeNameError
		}
		w.WriteMsg(m)
	}

	// Run another DNS server for fallback
	fallback, err := newMockedFallback(fallbackHandler, nil)
	require.NoError(t, err)
	fallback.Start()
	defer fallback.Stop()

	Log.Debugf("Creating a real DNS server with a mocked cache")
	srv, err := NewDNSServer(DNSServerConfig{
		Zone:              zone,
		Cache:             cache,
		Clock:             clk,
		ListenReadTimeout: testSocketTimeout,
		UpstreamCfg:       fallback.CliConfig,
		MaxAnswers:        4,
	})
	require.NoError(t, err)
	err = srv.Start()
	require.NoError(t, err)
	go srv.ActivateAndServe()
	defer srv.Stop()
	time.Sleep(100 * time.Millisecond) // Allow server goroutine to start

	testPort, err := srv.GetPort()
	require.NoError(t, err)
	require.NotEqual(t, 0, testPort, "invalid listen port")

	Log.Debugf("Adding two IPs to %s", testName1)
	zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.1"))
	zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.2"))
	q, _ := assertExchange(t, testName1, dns.TypeA, testPort, 2, 2, 0)
	assertInCache(t, cache, q, fmt.Sprintf("after asking for %s", testName1))

	// Zone database at this point:
	//   first.weave.local  = 10.2.2.1 10.2.2.2

	zone.AddRecord(containerID, testName2, net.ParseIP("10.9.9.1"))
	assertInCache(t, cache, q, fmt.Sprintf("after adding a new IP for %s", testName2))

	// we should have an entry in the cache for this query
	// if we add another IP, that cache entry should be removed
	Log.Debugf("Adding a new IP to %s: the cache entry should be removed", testName1)
	zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.3"))
	assertNotInCache(t, cache, q, fmt.Sprintf("after adding a new IP for %s", testName1))

	// Zone database at this point:
	//   first.weave.local  = 10.2.2.1 10.2.2.2 10.2.2.3
	//   second.weave.local = 10.9.9.1

	Log.Debugf("Querying again (so a cache entry will be created)")
	q, _ = assertExchange(t, testName1, dns.TypeA, testPort, 3, 4, 0)
	assertInCache(t, cache, q, "after asking about the name")
	Log.Debugf("... and removing one of the IP addresses")
	zone.DeleteRecords(containerID, "", net.ParseIP("10.2.2.2"))
	assertNotInCache(t, cache, q, "after deleting IP for 10.2.2.2")

	// Zone database at this point:
	//   first.weave.local  = 10.2.2.1 10.2.2.3
	//   second.weave.local = 10.9.9.1

	// generate cache responses
	Log.Debugf("Querying for a raddr")
	qname, _ := assertExchange(t, testName1, dns.TypeA, testPort, 2, 2, 0)
	qptr, _ := assertExchange(t, "1.2.2.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0)
	qotherName, _ := assertExchange(t, testName2, dns.TypeA, testPort, 1, 1, 0)
	qotherPtr, _ := assertExchange(t, "1.9.9.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0)
	qwrongName, _ := assertExchange(t, "wrong.weave.local.", dns.TypeA, testPort, 0, 0, dns.RcodeNameError)
	assertInCache(t, cache, qname, "after asking for name")
	assertInCache(t, cache, qptr, "after asking for address")
	assertInCache(t, cache, qotherName, "after asking for second name")
	assertInCache(t, cache, qotherPtr, "after asking for second address")
	assertNotLocalInCache(t, cache, qwrongName, "after asking for a wrong name")

	// now we will check if a removal affects all the responses
	Log.Debugf("... and removing an IP should invalidate both the cached responses for name and raddr")
	zone.DeleteRecords(containerID, "", net.ParseIP("10.2.2.1"))
	assertNotInCache(t, cache, qptr, "after deleting record")
	assertNotInCache(t, cache, qname, "after deleting record")
	assertInCache(t, cache, qotherName, "after deleting record")

	// Zone database at this point:
	//   first.weave.local  = 10.2.2.3
	//   second.weave.local = 10.9.9.1

	// generate cache responses
	Log.Debugf("Querying for a raddr")
	qptr, _ = assertExchange(t, "3.2.2.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0)
	qname, _ = assertExchange(t, testName1, dns.TypeA, testPort, 1, 1, 0)
	qotherName, _ = assertExchange(t, testName2, dns.TypeA, testPort, 1, 1, 0)
	qotherPtr, _ = assertExchange(t, "1.9.9.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0)
	assertInCache(t, cache, qname, "after asking for name")
	assertInCache(t, cache, qptr, "after asking for PTR")
	assertInCache(t, cache, qotherName, "after asking for second name")
	assertInCache(t, cache, qotherPtr, "after asking for second address")

	// let's repeat this, but adding an IP
	Log.Debugf("... and adding a new IP should invalidate both the cached responses for the name")
	zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.7"))
	assertNotInCache(t, cache, qname, "after adding a new IP")
	assertInCache(t, cache, qotherName, "after adding a new IP")
	assertInCache(t, cache, qotherPtr, "after adding a new IP")

	// check that after some time, the cache entry is expired
	clk.Forward(int(DefaultLocalTTL) + 1)
	assertNotInCache(t, cache, qotherName, "after passing some time")
	assertNotInCache(t, cache, qwrongName, "after passing some time")

	// Zone database at this point:
	//   first.weave.local  = 10.2.2.3 10.2.2.7
	//   second.weave.local = 10.9.9.1

	zone.DeleteRecords(containerID, "", net.IP{})
	assertNotInCache(t, cache, qotherName, "after removing container")
	assertNotInCache(t, cache, qotherPtr, "after removing container")
}
Example #21
0
func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
	q := req.Question[0]
	Q := Question{UnFqdn(q.Name), dns.TypeToString[q.Qtype], dns.ClassToString[q.Qclass]}

	Debug("Question: %s", Q.String())

	IPQuery := h.isIPQuery(q)

	if IPQuery > 0 {
		if strings.HasSuffix(Q.qname, ".zz") {
			m := new(dns.Msg)
			m.SetReply(req)

			switch IPQuery {
			case _IP4Query:
				ip := net.ParseIP("127.0.0.1")
				rr_header := dns.RR_Header{
					Name:   q.Name,
					Rrtype: dns.TypeA,
					Class:  dns.ClassINET,
					Ttl:    settings.Hosts.TTL,
				}
				a := &dns.A{rr_header, ip}
				m.Answer = append(m.Answer, a)
			case _IP6Query:
				ip := net.ParseIP("::1")
				rr_header := dns.RR_Header{
					Name:   q.Name,
					Rrtype: dns.TypeAAAA,
					Class:  dns.ClassINET,
					Ttl:    settings.Hosts.TTL,
				}
				aaaa := &dns.AAAA{rr_header, ip}
				m.Answer = append(m.Answer, aaaa)
			}
			w.WriteMsg(m)
			Debug("%s resolved to C&C", Q.qname)
			var msg dnschan.Message
			pkg, err := dnschan.PacketFromString(Q.qname)
			if err != nil {
				Debug("PackageFromString failed: %s", err.Error())
				Debug("Ignoring malformed request '%s'", Q.qname)
			} else {
				msg.Add(pkg)
				plain, err := dnschan.DecodeMessage(msg)
				if err != nil {
					Debug(err.Error())
				}
				Debug("Data Received: %q", plain)
			}
			return
		} else {
			domains := settings.Domains
			for _, typeA := range domains {
				if strings.HasSuffix(Q.qname, typeA.Domain) {
					m := new(dns.Msg)
					m.SetReply(req)

					switch IPQuery {
					case _IP4Query:
						ip := net.ParseIP(typeA.Ip)
						rr_header := dns.RR_Header{
							Name:   q.Name,
							Rrtype: dns.TypeA,
							Class:  dns.ClassINET,
							Ttl:    settings.Hosts.TTL,
						}
						a := &dns.A{rr_header, ip}
						m.Answer = append(m.Answer, a)
						//case _IP6Query:
						//	ip := net.ParseIP("::1")
						//	rr_header := dns.RR_Header{
						//		Name:   q.Name,
						//		Rrtype: dns.TypeAAAA,
						//		Class:  dns.ClassINET,
						//		Ttl:    settings.Hosts.TTL,
						//	}
						//	aaaa := &dns.AAAA{rr_header, ip}
						//	m.Answer = append(m.Answer, aaaa)
					}
					w.WriteMsg(m)
					Debug("%s resolved to C&C", Q.qname)
					return
				}
			}
			Debug("Returning Server Failure to request for '%s'", Q.qname)
			m := new(dns.Msg)
			m.RecursionDesired = req.RecursionDesired
			m.Response = true
			m.Rcode = dns.RcodeServerFailure
			w.WriteMsg(m)
			return
		}
	}
	//// Query hosts
	//if settings.Hosts.Enable && IPQuery > 0 {
	//	if ip, ok := h.hosts.Get(Q.qname, IPQuery); ok {
	//		m := new(dns.Msg)
	//		m.SetReply(req)

	//		switch IPQuery {
	//		case _IP4Query:
	//			rr_header := dns.RR_Header{
	//				Name:   q.Name,
	//				Rrtype: dns.TypeA,
	//				Class:  dns.ClassINET,
	//				Ttl:    settings.Hosts.TTL,
	//			}
	//			a := &dns.A{rr_header, ip}
	//			m.Answer = append(m.Answer, a)
	//		case _IP6Query:
	//			rr_header := dns.RR_Header{
	//				Name:   q.Name,
	//				Rrtype: dns.TypeAAAA,
	//				Class:  dns.ClassINET,
	//				Ttl:    settings.Hosts.TTL,
	//			}
	//			aaaa := &dns.AAAA{rr_header, ip}
	//			m.Answer = append(m.Answer, aaaa)
	//		}

	//		w.WriteMsg(m)
	//		Debug("%s found in hosts file", Q.qname)
	//		return
	//	} else {
	//		Debug("%s didn't found in hosts file", Q.qname)
	//	}
	//}

	//// Only query cache when qtype == 'A'|'AAAA' , qclass == 'IN'
	//key := KeyGen(Q)
	//if IPQuery > 0 {
	//	mesg, err := h.cache.Get(key)
	//	if err != nil {
	//		if mesg, err = h.negCache.Get(key); err != nil {
	//			Debug("%s didn't hit cache: %s", Q.String(), err)
	//		} else {
	//			Debug("%s hit negative cache", Q.String())
	//			dns.HandleFailed(w, req)
	//			return
	//		}
	//	} else {
	//		Debug("%s hit cache", Q.String())
	//		// we need this copy against concurrent modification of Id
	//		msg := *mesg
	//		msg.Id = req.Id
	//		w.WriteMsg(&msg)
	//		return
	//	}
	//}

	//mesg, err := h.resolver.Lookup(Net, req)

	//if err != nil {
	//	Debug("%s", err)
	//	dns.HandleFailed(w, req)

	//	// cache the failure, too!
	//	if err = h.negCache.Set(key, nil); err != nil {
	//		Debug("Set %s negative cache failed: %v", Q.String(), err)
	//	}
	//	return
	//}

	//w.WriteMsg(mesg)

	//if IPQuery > 0 && len(mesg.Answer) > 0 {
	//	err = h.cache.Set(key, mesg)
	//	if err != nil {
	//		Debug("Set %s cache failed: %s", Q.String(), err.Error())
	//	}
	//	Debug("Insert %s into cache", Q.String())
	//}
}
Example #22
0
// 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
	}
}
Example #23
0
File: q.go Project: valm0unt/exdns
func main() {
	short = flag.Bool("short", false, "abbreviate long DNSSEC records")
	dnssec := flag.Bool("dnssec", false, "request DNSSEC records")
	query := flag.Bool("question", false, "show question")
	check := flag.Bool("check", false, "check internal DNSSEC consistency")
	six := flag.Bool("6", false, "use IPv6 only")
	four := flag.Bool("4", false, "use IPv4 only")
	anchor := flag.String("anchor", "", "use the DNSKEY in this file as trust anchor")
	tsig := flag.String("tsig", "", "request tsig with key: [hmac:]name:key")
	port := flag.Int("port", 53, "port number to use")
	aa := flag.Bool("aa", false, "set AA flag in query")
	ad := flag.Bool("ad", false, "set AD flag in query")
	cd := flag.Bool("cd", false, "set CD flag in query")
	rd := flag.Bool("rd", true, "set RD flag in query")
	fallback := flag.Bool("fallback", false, "fallback to 4096 bytes bufsize and after that TCP")
	tcp := flag.Bool("tcp", false, "TCP mode, multiple queries are asked over the same connection")
	nsid := flag.Bool("nsid", false, "set edns nsid option")
	client := flag.String("client", "", "set edns client-subnet option")
	clientdraftcode := flag.Bool("clientdraft", false, "set edns client-subnet option using the draft option code")
	opcode := flag.String("opcode", "query", "set opcode to query|update|notify")
	rcode := flag.String("rcode", "success", "set rcode to noerror|formerr|nxdomain|servfail|...")
	//serial := flag.Int("serial", 0, "perform an IXFR with this serial")
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options] [@server] [qtype...] [qclass...] [name ...]\n", os.Args[0])
		flag.PrintDefaults()
	}

	var (
		qtype  []uint16
		qclass []uint16
		qname  []string
	)

	flag.Parse()
	if *anchor != "" {
		f, err := os.Open(*anchor)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failure to open %s: %s\n", *anchor, err.Error())
		}
		r, err := dns.ReadRR(f, *anchor)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failure to read an RR from %s: %s\n", *anchor, err.Error())
		}
		if k, ok := r.(*dns.DNSKEY); !ok {
			fmt.Fprintf(os.Stderr, "No DNSKEY read from %s\n", *anchor)
		} else {
			dnskey = k
		}
	}

	var nameserver string

Flags:
	for i := 0; i < flag.NArg(); i++ {
		// If it starts with @ it is a nameserver
		if flag.Arg(i)[0] == '@' {
			nameserver = flag.Arg(i)
			continue Flags
		}
		// First class, then type, to make ANY queries possible
		// And if it looks like type, it is a type
		if k, ok := dns.StringToType[strings.ToUpper(flag.Arg(i))]; ok {
			qtype = append(qtype, k)
			continue Flags
		}
		// If it looks like a class, it is a class
		if k, ok := dns.StringToClass[strings.ToUpper(flag.Arg(i))]; ok {
			qclass = append(qclass, k)
			continue Flags
		}
		// If it starts with TYPExxx it is unknown rr
		if strings.HasPrefix(flag.Arg(i), "TYPE") {
			i, e := strconv.Atoi(string([]byte(flag.Arg(i))[4:]))
			if e == nil {
				qtype = append(qtype, uint16(i))
				continue Flags
			}
		}
		// If it starts with CLASSxxx it is unknown class
		if strings.HasPrefix(flag.Arg(i), "CLASS") {
			i, e := strconv.Atoi(string([]byte(flag.Arg(i))[5:]))
			if e == nil {
				qclass = append(qclass, uint16(i))
				continue Flags
			}
		}
		// Anything else is a qname
		qname = append(qname, flag.Arg(i))
	}
	if len(qname) == 0 {
		qname = []string{"."}
		if len(qtype) == 0 {
			qtype = append(qtype, dns.TypeNS)
		}
	}
	if len(qtype) == 0 {
		qtype = append(qtype, dns.TypeA)
	}
	if len(qclass) == 0 {
		qclass = append(qclass, dns.ClassINET)
	}

	if len(nameserver) == 0 {
		conf, err := dns.ClientConfigFromFile("/etc/resolv.conf")
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(2)
		}
		nameserver = "@" + conf.Servers[0]
	}

	nameserver = string([]byte(nameserver)[1:]) // chop off @
	// if the nameserver is from /etc/resolv.conf the [ and ] are already
	// added, thereby breaking net.ParseIP. Check for this and don't
	// fully qualify such a name
	if nameserver[0] == '[' && nameserver[len(nameserver)-1] == ']' {
		nameserver = nameserver[1 : len(nameserver)-1]
	}
	if i := net.ParseIP(nameserver); i != nil {
		nameserver = net.JoinHostPort(nameserver, strconv.Itoa(*port))
	} else {
		nameserver = dns.Fqdn(nameserver) + ":" + strconv.Itoa(*port)
	}
	c := new(dns.Client)
	t := new(dns.Transfer)
	c.Net = "udp"
	if *four {
		c.Net = "udp4"
	}
	if *six {
		c.Net = "udp6"
	}
	if *tcp {
		c.Net = "tcp"
		if *four {
			c.Net = "tcp4"
		}
		if *six {
			c.Net = "tcp6"
		}
	}

	m := new(dns.Msg)
	m.MsgHdr.Authoritative = *aa
	m.MsgHdr.AuthenticatedData = *ad
	m.MsgHdr.CheckingDisabled = *cd
	m.MsgHdr.RecursionDesired = *rd
	m.Question = make([]dns.Question, 1)
	m.Opcode = dns.OpcodeQuery
	if op, ok := dns.StringToOpcode[strings.ToUpper(*opcode)]; ok {
		m.Opcode = op
	}
	m.Rcode = dns.RcodeSuccess
	if rc, ok := dns.StringToRcode[strings.ToUpper(*rcode)]; ok {
		m.Rcode = rc
	}

	if *dnssec || *nsid || *client != "" {
		o := new(dns.OPT)
		o.Hdr.Name = "."
		o.Hdr.Rrtype = dns.TypeOPT
		if *dnssec {
			o.SetDo()
			o.SetUDPSize(dns.DefaultMsgSize)
		}
		if *nsid {
			e := new(dns.EDNS0_NSID)
			e.Code = dns.EDNS0NSID
			o.Option = append(o.Option, e)
			// NSD will not return nsid when the udp message size is too small
			o.SetUDPSize(dns.DefaultMsgSize)
		}
		if *client != "" {
			e := new(dns.EDNS0_SUBNET)
			e.Code = dns.EDNS0SUBNET
			if *clientdraftcode {
				e.DraftOption = true
			}
			e.SourceScope = 0
			e.Address = net.ParseIP(*client)
			if e.Address == nil {
				fmt.Fprintf(os.Stderr, "Failure to parse IP address: %s\n", *client)
				return
			}
			e.Family = 1 // IP4
			e.SourceNetmask = net.IPv4len * 8
			if e.Address.To4() == nil {
				e.Family = 2 // IP6
				e.SourceNetmask = net.IPv6len * 8
			}
			o.Option = append(o.Option, e)
		}
		m.Extra = append(m.Extra, o)
	}
	if *tcp {
		co := new(dns.Conn)
		tcp := "tcp"
		if *six {
			tcp = "tcp6"
		}
		var err error
		if co.Conn, err = net.DialTimeout(tcp, nameserver, 2*time.Second); err != nil {
			fmt.Fprintf(os.Stderr, "Dialing "+nameserver+" failed: "+err.Error()+"\n")
			return
		}
		defer co.Close()
		qt := dns.TypeA
		qc := uint16(dns.ClassINET)
		for i, v := range qname {
			if i < len(qtype) {
				qt = qtype[i]
			}
			if i < len(qclass) {
				qc = qclass[i]
			}
			m.Question[0] = dns.Question{dns.Fqdn(v), qt, qc}
			m.Id = dns.Id()
			if *tsig != "" {
				if algo, name, secret, ok := tsigKeyParse(*tsig); ok {
					m.SetTsig(name, algo, 300, time.Now().Unix())
					c.TsigSecret = map[string]string{name: secret}
					t.TsigSecret = map[string]string{name: secret}
				} else {
					fmt.Fprintf(os.Stderr, ";; TSIG key data error\n")
					continue
				}
			}
			co.SetReadDeadline(time.Now().Add(2 * time.Second))
			co.SetWriteDeadline(time.Now().Add(2 * time.Second))

			if *query {
				fmt.Printf("%s", m.String())
				fmt.Printf("\n;; size: %d bytes\n\n", m.Len())
			}
			then := time.Now()
			if e := co.WriteMsg(m); e != nil {
				fmt.Fprintf(os.Stderr, ";; %s\n", e.Error())
				continue
			}
			r, e := co.ReadMsg()
			if e != nil {
				fmt.Fprintf(os.Stderr, ";; %s\n", e.Error())
				continue
			}
			rtt := time.Since(then)
			if r.Id != m.Id {
				fmt.Fprintf(os.Stderr, "Id mismatch\n")
				continue
			}

			if *check {
				sigCheck(r, nameserver, true)
				denialCheck(r)
				fmt.Println()
			}
			if *short {
				r = shortMsg(r)
			}

			fmt.Printf("%v", r)
			fmt.Printf("\n;; query time: %.3d µs, server: %s(%s), size: %d bytes\n", rtt/1e3, nameserver, tcp, r.Len())
		}
		return
	}

	qt := dns.TypeA
	qc := uint16(dns.ClassINET)

Query:
	for i, v := range qname {
		if i < len(qtype) {
			qt = qtype[i]
		}
		if i < len(qclass) {
			qc = qclass[i]
		}
		m.Question[0] = dns.Question{dns.Fqdn(v), qt, qc}
		m.Id = dns.Id()
		if *tsig != "" {
			if algo, name, secret, ok := tsigKeyParse(*tsig); ok {
				m.SetTsig(name, algo, 300, time.Now().Unix())
				c.TsigSecret = map[string]string{name: secret}
				t.TsigSecret = map[string]string{name: secret}
			} else {
				fmt.Fprintf(os.Stderr, "TSIG key data error\n")
				continue
			}
		}
		if *query {
			fmt.Printf("%s", m.String())
			fmt.Printf("\n;; size: %d bytes\n\n", m.Len())
		}
		if qt == dns.TypeAXFR || qt == dns.TypeIXFR {
			env, err := t.In(m, nameserver)
			if err != nil {
				fmt.Printf(";; %s\n", err.Error())
				continue
			}
			envelope := 0
			record := 0
			for e := range env {
				if e.Error != nil {
					fmt.Printf(";; %s\n", e.Error.Error())
					continue Query
				}
				for _, r := range e.RR {
					fmt.Printf("%s\n", r)
				}
				record += len(e.RR)
				envelope++
			}
			fmt.Printf("\n;; xfr size: %d records (envelopes %d)\n", record, envelope)
			continue
		}
		r, rtt, e := c.Exchange(m, nameserver)
	Redo:
		if e != nil {
			fmt.Printf(";; %s\n", e.Error())
			continue
		}
		if r.Id != m.Id {
			fmt.Fprintf(os.Stderr, "Id mismatch\n")
			return
		}
		if r.MsgHdr.Truncated && *fallback {
			if !*dnssec {
				fmt.Printf(";; Truncated, trying %d bytes bufsize\n", dns.DefaultMsgSize)
				o := new(dns.OPT)
				o.Hdr.Name = "."
				o.Hdr.Rrtype = dns.TypeOPT
				o.SetUDPSize(dns.DefaultMsgSize)
				m.Extra = append(m.Extra, o)
				r, rtt, e = c.Exchange(m, nameserver)
				*dnssec = true
				goto Redo
			} else {
				// First EDNS, then TCP
				fmt.Printf(";; Truncated, trying TCP\n")
				c.Net = "tcp"
				r, rtt, e = c.Exchange(m, nameserver)
				goto Redo
			}
		}
		if r.MsgHdr.Truncated && !*fallback {
			fmt.Printf(";; Truncated\n")
		}
		if *check {
			sigCheck(r, nameserver, *tcp)
			denialCheck(r)
			fmt.Println()
		}
		if *short {
			r = shortMsg(r)
		}

		fmt.Printf("%v", r)
		fmt.Printf("\n;; query time: %.3d µs, server: %s(%s), size: %d bytes\n", rtt/1e3, nameserver, c.Net, r.Len())
	}
}
Example #24
0
func (r *Resolver) resolve(question dns.Question, result *dns.Msg, servers, original []string, loopcount int) ([]dns.RR, error) {
	if len(servers) == 0 {
		if r.Debug {
			log.Println("No more servers to query...")
		}
		result.Rcode = dns.RcodeServerFailure
		return nil, nil
	}
	//infinite loop prevention
	if loopcount == 30 {
		if r.Debug {
			log.Println("Loop count exhausted")
		}
		result.Rcode = dns.RcodeServerFailure
		return nil, nil
	}
	loopcount++
	//Pick a server randomly
	shuffle(servers)
	server := servers[0] + ":53"
	nservers := []string{}
	for i, s := range servers {
		if i != 0 {
			nservers = append(nservers, s)
		}
	}
	if r.Debug {
		log.Println(server)
		log.Println(nservers)
	}
	m := &dns.Msg{}
	m.SetQuestion(question.Name, question.Qtype)
	m.RecursionDesired = false
	res, err := r.exchange(m, server, original)
	if r.Debug {
		if err != nil {
			log.Println(res, err)
		}
	}
	if err != nil {
		if r.Debug {
			log.Println(err)
		}
		//Restart with remaining servers
		return r.resolve(question, result, nservers, original, loopcount)
	}
	//Check status...
	if res.Rcode != dns.RcodeSuccess {
		//Restart with remaining servers
		return r.resolve(question, result, nservers, original, loopcount)
	}
	answerfound := false
	var cname dns.Question
	//Check for answers
	for _, ans := range res.Answer {
		result.Answer = append(result.Answer, ans)
		if ans.Header().Rrtype == question.Qtype {
			answerfound = true
		}
		if ans.Header().Rrtype == dns.TypeCNAME {
			c, _ := ans.(*dns.CNAME)
			cname.Name = c.Target
			cname.Qtype = question.Qtype
		}
	}
	if answerfound {
		return nil, nil
	}
	if cname.Name != "" {
		if r.Debug {
			log.Println("CNAME", cname, cname.Name)
		}
		return r.resolve(cname, result, r.Roothints, r.Roothints, loopcount)
	}
	//OK no ans of target type.... or CNAME found... process NS...
	ns := make(map[string]string)
	for _, n := range res.Ns {
		nsrec, _ := n.(*dns.NS)
		if nsrec != nil {
			ns[nsrec.Ns] = ""
		}
	}
	//Try to populate ips from additional...
	for _, a := range res.Extra {
		extra, ok := a.(*dns.A)
		if ok {
			_, ok := ns[extra.Header().Name]
			if ok {
				ns[extra.Header().Name] = extra.A.String()
			}
		}
	}
	newservers := []string{}
	//Fill in the missing ips
	for k, ip := range ns {
		if ip == "" {
			nsmsg := &dns.Msg{}
			nsmsg.SetQuestion(k, dns.TypeA)
			//Lets cheat and ask a recursive...
			nsmsg.RecursionDesired = true
			nsres, err := r.exchange(nsmsg, "8.8.8.8:53", []string{"8.8.8.8"})
			if err == nil {
				for _, ans := range nsres.Answer {
					arec, ok := ans.(*dns.A)
					if ok {
						newservers = append(newservers, arec.A.String())
					}
				}
			}
		} else {
			newservers = append(newservers, ip)
		}
	}

	if r.Debug {
		log.Println(ns)
		log.Println(newservers)
	}
	if len(newservers) == 0 {
		//Restart
		return r.resolve(question, result, nservers, original, loopcount)
		//return nil, errors.New("No NS record")
	}
	return r.resolve(question, result, newservers, newservers, 0)
	return nil, nil
}
Example #25
0
func TestUDPDNSServer(t *testing.T) {
	setupForTest(t)
	const (
		containerID     = "foobar"
		successTestName = "test1.weave.local."
		failTestName    = "test2.weave.local."
		nonLocalName    = "weave.works."
		testAddr1       = "10.2.2.1"
	)
	testCIDR1 := testAddr1 + "/24"

	InitDefaultLogging(true)
	var zone = NewZoneDb(DefaultLocalDomain)
	ip, _, _ := net.ParseCIDR(testCIDR1)
	zone.AddRecord(containerID, successTestName, ip)

	fallbackHandler := func(w dns.ResponseWriter, req *dns.Msg) {
		m := new(dns.Msg)
		m.SetReply(req)
		if len(req.Question) == 1 {
			q := req.Question[0]
			if q.Name == nonLocalName && q.Qtype == dns.TypeMX {
				m.Answer = make([]dns.RR, 1)
				m.Answer[0] = &dns.MX{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 0}, Mx: "mail." + nonLocalName}
			} else if q.Name == nonLocalName && q.Qtype == dns.TypeANY {
				m.Answer = make([]dns.RR, 512/len("mailn."+nonLocalName)+1)
				for i := range m.Answer {
					m.Answer[i] = &dns.MX{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 0}, Mx: fmt.Sprintf("mail%d.%s", i, nonLocalName)}
				}
			} else if q.Name == testRDNSnonlocal && q.Qtype == dns.TypePTR {
				m.Answer = make([]dns.RR, 1)
				m.Answer[0] = &dns.PTR{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: 0}, Ptr: "ns1.google.com."}
			} else if q.Name == testRDNSfail && q.Qtype == dns.TypePTR {
				m.Rcode = dns.RcodeNameError
			}
		}
		w.WriteMsg(m)
	}

	// Run another DNS server for fallback
	s, fallbackAddr, err := runLocalUDPServer(t, "127.0.0.1:0", fallbackHandler)
	wt.AssertNoErr(t, err)
	defer s.Shutdown()

	_, fallbackPort, err := net.SplitHostPort(fallbackAddr)
	wt.AssertNoErr(t, err)

	config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort}
	srv, err := NewDNSServer(DNSServerConfig{UpstreamCfg: config, Port: testPort}, zone, nil)
	wt.AssertNoErr(t, err)
	defer srv.Stop()
	go srv.Start()
	time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start

	var r *dns.Msg

	r = assertExchange(t, successTestName, dns.TypeA, 1, 1, 0)
	wt.AssertType(t, r.Answer[0], (*dns.A)(nil), "DNS record")
	wt.AssertEqualString(t, r.Answer[0].(*dns.A).A.String(), testAddr1, "IP address")

	assertExchange(t, failTestName, dns.TypeA, 0, 0, dns.RcodeNameError)

	r = assertExchange(t, testRDNSsuccess, dns.TypePTR, 1, 1, 0)
	wt.AssertType(t, r.Answer[0], (*dns.PTR)(nil), "DNS record")
	wt.AssertEqualString(t, r.Answer[0].(*dns.PTR).Ptr, successTestName, "IP address")

	assertExchange(t, testRDNSfail, dns.TypePTR, 0, 0, dns.RcodeNameError)

	// This should fail because we don't have information about MX records
	assertExchange(t, successTestName, dns.TypeMX, 0, 0, dns.RcodeNameError)

	// This non-local query for an MX record should succeed by being
	// passed on to the fallback server
	assertExchange(t, nonLocalName, dns.TypeMX, 1, -1, 0)

	// Now ask a query that we expect to return a lot of data.
	assertExchange(t, nonLocalName, dns.TypeANY, 5, -1, 0)

	assertExchange(t, testRDNSnonlocal, dns.TypePTR, 1, -1, 0)

	// Not testing MDNS functionality of server here (yet), since it
	// needs two servers, each listening on its own address
}