Add some docs about normalize.Host and normalize.Name. They are used correctly in the middleware even though they are somewhat confusing, esp when you copy from ServerBlockKeys in your middleware.
137 lines
3.7 KiB
Go
137 lines
3.7 KiB
Go
package middleware
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
// See core/dnsserver/address.go - we should unify these two impls.
|
|
|
|
// Zones respresents a lists of zone names.
|
|
type Zones []string
|
|
|
|
// Matches checks is qname is a subdomain of any of the zones in z. The match
|
|
// will return the most specific zones that matches other. The empty string
|
|
// signals a not found condition.
|
|
func (z Zones) Matches(qname string) string {
|
|
zone := ""
|
|
for _, zname := range z {
|
|
if dns.IsSubDomain(zname, qname) {
|
|
// We want the *longest* matching zone, otherwise we may end up in a parent
|
|
if len(zname) > len(zone) {
|
|
zone = zname
|
|
}
|
|
}
|
|
}
|
|
return zone
|
|
}
|
|
|
|
// Normalize fully qualifies all zones in z. The zones in Z must be domain names, without
|
|
// a port or protocol prefix.
|
|
func (z Zones) Normalize() {
|
|
for i := range z {
|
|
z[i] = Name(z[i]).Normalize()
|
|
}
|
|
}
|
|
|
|
// Name represents a domain name.
|
|
type Name string
|
|
|
|
// Matches checks to see if other is a subdomain (or the same domain) of n.
|
|
// This method assures that names can be easily and consistently matched.
|
|
func (n Name) Matches(child string) bool {
|
|
if dns.Name(n) == dns.Name(child) {
|
|
return true
|
|
}
|
|
return dns.IsSubDomain(string(n), child)
|
|
}
|
|
|
|
// Normalize lowercases and makes n fully qualified.
|
|
func (n Name) Normalize() string { return strings.ToLower(dns.Fqdn(string(n))) }
|
|
|
|
type (
|
|
// Host represents a host from the Corefile, may contain port.
|
|
Host string
|
|
)
|
|
|
|
// Normalize will return the host portion of host, stripping
|
|
// of any port or transport. The host will also be fully qualified and lowercased.
|
|
func (h Host) Normalize() string {
|
|
|
|
s := string(h)
|
|
|
|
switch {
|
|
case strings.HasPrefix(s, TransportTLS+"://"):
|
|
s = s[len(TransportTLS+"://"):]
|
|
case strings.HasPrefix(s, TransportDNS+"://"):
|
|
s = s[len(TransportDNS+"://"):]
|
|
case strings.HasPrefix(s, TransportGRPC+"://"):
|
|
s = s[len(TransportGRPC+"://"):]
|
|
}
|
|
|
|
// The error can be ignore here, because this function is called after the corefile
|
|
// has already been vetted.
|
|
host, _, _ := SplitHostPort(s)
|
|
return Name(host).Normalize()
|
|
}
|
|
|
|
// SplitHostPort splits s up in a host and port portion, taking reverse address notation into account.
|
|
// String the string s should *not* be prefixed with any protocols, i.e. dns://
|
|
func SplitHostPort(s string) (host, port string, err error) {
|
|
// If there is: :[0-9]+ on the end we assume this is the port. This works for (ascii) domain
|
|
// names and our reverse syntax, which always needs a /mask *before* the port.
|
|
// So from the back, find first colon, and then check if its a number.
|
|
host = s
|
|
|
|
colon := strings.LastIndex(s, ":")
|
|
if colon == len(s)-1 {
|
|
return "", "", fmt.Errorf("expecting data after last colon: %q", s)
|
|
}
|
|
if colon != -1 {
|
|
if p, err := strconv.Atoi(s[colon+1:]); err == nil {
|
|
port = strconv.Itoa(p)
|
|
host = s[:colon]
|
|
}
|
|
}
|
|
|
|
// TODO(miek): this should take escaping into account.
|
|
if len(host) > 255 {
|
|
return "", "", fmt.Errorf("specified zone is too long: %d > 255", len(host))
|
|
}
|
|
|
|
_, d := dns.IsDomainName(host)
|
|
if !d {
|
|
return "", "", fmt.Errorf("zone is not a valid domain name: %s", host)
|
|
}
|
|
|
|
// Check if it parses as a reverse zone, if so we use that. Must be fully
|
|
// specified IP and mask and mask % 8 = 0.
|
|
ip, net, err := net.ParseCIDR(host)
|
|
if err == nil {
|
|
if rev, e := dns.ReverseAddr(ip.String()); e == nil {
|
|
ones, bits := net.Mask.Size()
|
|
if (bits-ones)%8 == 0 {
|
|
offset, end := 0, false
|
|
for i := 0; i < (bits-ones)/8; i++ {
|
|
offset, end = dns.NextLabel(rev, offset)
|
|
if end {
|
|
break
|
|
}
|
|
}
|
|
host = rev[offset:]
|
|
}
|
|
}
|
|
}
|
|
return host, port, nil
|
|
}
|
|
|
|
// Duplicated from core/dnsserver/address.go !
|
|
const (
|
|
TransportDNS = "dns"
|
|
TransportTLS = "tls"
|
|
TransportGRPC = "grpc"
|
|
)
|