Exemple #1
0
// TrimDomainName trims origin from s if s is a subdomain.
// This function will never return "", but returns "@" instead (@ represents the apex (bare) domain).
func TrimDomainName(s, origin string) string {
	// An apex (bare) domain is always returned as "@".
	// If the return value ends in a ".", the domain was not the suffix.
	// origin can end in "." or not. Either way the results should be the same.

	if len(s) == 0 {
		return "@" // Return the apex (@) rather than "".
	}
	// Someone is using TrimDomainName(s, ".") to remove a dot if it exists.
	if origin == "." {
		return strings.TrimSuffix(s, origin)
	}

	// Dude, you aren't even if the right subdomain!
	if !dns.IsSubDomain(origin, s) {
		return s
	}

	slabels := dns.Split(s)
	olabels := dns.Split(origin)
	m := dns.CompareDomainName(s, origin)
	if len(olabels) == m {
		if len(olabels) == len(slabels) {
			return "@" // origin == s
		}
		if (s[0] == '.') && (len(slabels) == (len(olabels) + 1)) {
			return "@" // TrimDomainName(".foo.", "foo.")
		}
	}

	// Return the first (len-m) labels:
	return s[:slabels[len(slabels)-m]-1]
}
Exemple #2
0
// Denial creates (if needed) NSEC3 records that are included in the reply.
func (s *server) Denial(m *dns.Msg) {
	if m.Rcode == dns.RcodeNameError {
		// qname nsec
		nsec1 := s.NewNSEC3(m.Question[0].Name)
		m.Ns = append(m.Ns, nsec1)
		// wildcard nsec
		idx := dns.Split(m.Question[0].Name)
		wildcard := "*." + m.Question[0].Name[idx[0]:]
		nsec2 := s.NewNSEC3(wildcard)
		if nsec1.Hdr.Name != nsec2.Hdr.Name || nsec1.NextDomain != nsec2.NextDomain {
			// different NSEC3, add it
			m.Ns = append(m.Ns, nsec2)
		}
	}
	if m.Rcode == dns.RcodeSuccess && len(m.Ns) == 1 {
		if _, ok := m.Ns[0].(*dns.SOA); ok {
			m.Ns = append(m.Ns, s.NewNSEC3(m.Question[0].Name))
		}
	}
}
Exemple #3
0
func (s *server) Denial(m *dns.Msg) {
	if m.Rcode == dns.RcodeNameError {
		// ce is qname minus the left label
		idx := dns.Split(m.Question[0].Name)
		ce := m.Question[0].Name[idx[1]:]

		nsec3ce, nsec3wildcard := newNSEC3CEandWildcard(s.config.Domain, ce, s.config.MinTtl)
		// Add ce and wildcard
		m.Ns = append(m.Ns, nsec3ce)
		m.Ns = append(m.Ns, nsec3wildcard)
		// Deny Qname nsec3
		m.Ns = append(m.Ns, s.newNSEC3NameError(m.Question[0].Name))
	}
	if m.Rcode == dns.RcodeSuccess && len(m.Ns) == 1 {
		// NODATA
		if _, ok := m.Ns[0].(*dns.SOA); ok {
			m.Ns = append(m.Ns, s.newNSEC3NoData(m.Question[0].Name))
		}
	}
}
Exemple #4
0
// FindZoneByFqdn determines the zone apex for the given fqdn by recursing up the
// domain labels until the nameserver returns a SOA record in the answer section.
func FindZoneByFqdn(fqdn string, nameservers []string) (string, error) {
	// Do we have it cached?
	if zone, ok := fqdnToZone[fqdn]; ok {
		return zone, nil
	}

	labelIndexes := dns.Split(fqdn)
	for _, index := range labelIndexes {
		domain := fqdn[index:]
		// Give up if we have reached the TLD
		if isTLD(domain) {
			break
		}

		in, err := dnsQuery(domain, dns.TypeSOA, nameservers, true)
		if err != nil {
			return "", err
		}

		// Any response code other than NOERROR and NXDOMAIN is treated as error
		if in.Rcode != dns.RcodeNameError && in.Rcode != dns.RcodeSuccess {
			return "", fmt.Errorf("Unexpected response code '%s' for %s",
				dns.RcodeToString[in.Rcode], domain)
		}

		// Check if we got a SOA RR in the answer section
		if in.Rcode == dns.RcodeSuccess {
			for _, ans := range in.Answer {
				if soa, ok := ans.(*dns.SOA); ok {
					zone := soa.Hdr.Name
					fqdnToZone[fqdn] = zone
					return zone, nil
				}
			}
		}
	}

	return "", fmt.Errorf("Could not find the start of authority")
}
Exemple #5
0
// NSEC3 Helper
func verifyNameError3(nsec3 []dns.RR, qname string, qtype uint16) error {
	indx := dns.Split(qname)
	ce := "" // Closest Encloser
	nc := "" // Next Closer
	wc := "" // Source of Synthesis (wildcard)
ClosestEncloser:
	for i := 0; i < len(indx); i++ {
		for j := 0; j < len(nsec3); j++ {
			if nsec3[j].(*dns.NSEC3).Match(qname[indx[i]:]) {
				ce = qname[indx[i]:]
				wc = "*." + ce
				if i == 0 {
					nc = qname
				} else {
					nc = qname[indx[i-1]:]
				}
				break ClosestEncloser
			}
		}
	}
	if ce == "" {
		return dns.ErrSig // ErrNoMatchingNSEC3
	}
	covered := 0 // Both nc and wc must be covered
	for i := 0; i < len(nsec3); i++ {
		if nsec3[i].(*dns.NSEC3).Cover(nc) {
			covered++
		}
		if nsec3[i].(*dns.NSEC3).Cover(wc) {
			covered++
		}
	}
	if covered != 2 {
		return dns.ErrSig
	}
	return nil
}
Exemple #6
0
// NSEC3 Helper
func denial3(nsec3 []dns.RR, in *dns.Msg) {
	qname := in.Question[0].Name
	qtype := in.Question[0].Qtype
	switch in.Rcode {
	case dns.RcodeSuccess:
		// qname should match nsec3, type should not be in bitmap
		match := nsec3[0].(*dns.NSEC3).Match(qname)
		if !match {
			fmt.Printf(";- Denial, owner name does not match qname\n")
			fmt.Printf(";- Denial, failed authenticated denial of existence proof for no data\n")
			return
		}
		for _, t := range nsec3[0].(*dns.NSEC3).TypeBitMap {
			if t == qtype {
				fmt.Printf(";- Denial, found type, %d, in bitmap\n", qtype)
				fmt.Printf(";- Denial, failed authenticated denial of existence proof for no data\n")
				return
			}
			if t > qtype { // ordered list, bail out, because not found
				break
			}
		}
		// Some success data printed here
		fmt.Printf(";+ Denial, matching record, %s, (%s) found and type %s denied\n", qname,
			strings.ToLower(dns.HashName(qname, nsec3[0].(*dns.NSEC3).Hash, nsec3[0].(*dns.NSEC3).Iterations, nsec3[0].(*dns.NSEC3).Salt)),
			dns.TypeToString[qtype])
		fmt.Printf(";+ Denial, secure authenticated denial of existence proof for no data\n")
		return
	case dns.RcodeNameError: // NXDOMAIN Proof
		indx := dns.Split(qname)
		ce := "" // Closest Encloser
		nc := "" // Next Closer
		wc := "" // Source of Synthesis (wildcard)
	ClosestEncloser:
		for i := 0; i < len(indx); i++ {
			for j := 0; j < len(nsec3); j++ {
				if nsec3[j].(*dns.NSEC3).Match(qname[indx[i]:]) {
					ce = qname[indx[i]:]
					wc = "*." + ce
					if i == 0 {
						nc = qname
					} else {
						nc = qname[indx[i-1]:]
					}
					break ClosestEncloser
				}
			}
		}
		if ce == "" {
			fmt.Printf(";- Denial, closest encloser not found\n")
			return
		}
		fmt.Printf(";+ Denial, closest encloser, %s (%s)\n", ce,
			strings.ToLower(dns.HashName(ce, nsec3[0].(*dns.NSEC3).Hash, nsec3[0].(*dns.NSEC3).Iterations, nsec3[0].(*dns.NSEC3).Salt)))
		covered := 0 // Both nc and wc must be covered
		for i := 0; i < len(nsec3); i++ {
			if nsec3[i].(*dns.NSEC3).Cover(nc) {
				fmt.Printf(";+ Denial, next closer %s (%s), covered by %s -> %s\n", nc, nsec3[i].Header().Name, nsec3[i].(*dns.NSEC3).NextDomain,
					strings.ToLower(dns.HashName(ce, nsec3[0].(*dns.NSEC3).Hash, nsec3[0].(*dns.NSEC3).Iterations, nsec3[0].(*dns.NSEC3).Salt)))
				covered++
			}
			if nsec3[i].(*dns.NSEC3).Cover(wc) {
				fmt.Printf(";+ Denial, source of synthesis %s (%s), covered by %s -> %s\n", wc, nsec3[i].Header().Name, nsec3[i].(*dns.NSEC3).NextDomain,
					strings.ToLower(dns.HashName(ce, nsec3[0].(*dns.NSEC3).Hash, nsec3[0].(*dns.NSEC3).Iterations, nsec3[0].(*dns.NSEC3).Salt)))
				covered++
			}
		}
		if covered != 2 {
			fmt.Printf(";- Denial, too many, %d, covering records\n", covered)
			fmt.Printf(";- Denial, failed authenticated denial of existence proof for name error\n")
			return
		}
		fmt.Printf(";+ Denial, secure authenticated denial of existence proof for name error\n")
		return
	}
}