diff --git a/network/netutils/cleandns.go b/network/netutils/cleandns.go index f9487664..7df738d0 100644 --- a/network/netutils/cleandns.go +++ b/network/netutils/cleandns.go @@ -2,13 +2,57 @@ package netutils import ( "regexp" + + "github.com/miekg/dns" ) var ( - cleanDomainRegex = regexp.MustCompile(`^((xn--)?[a-z0-9-_]{0,61}[a-z0-9]{1,1}\.)*(xn--)?([a-z0-9-]{1,61}|[a-z0-9-]{1,30}\.[a-z]{2,}\.)$`) + cleanDomainRegex = regexp.MustCompile( + `^` + // match beginning + `(` + // start subdomain group + `(xn--)?` + // idn prefix + `[a-z0-9_-]{1,63}` + // main chunk + `\.` + // ending with a dot + `)*` + // end subdomain group, allow any number of subdomains + `(xn--)?` + // TLD idn prefix + `[a-z0-9_-]{2,63}` + // TLD main chunk with at least two characters + `\.` + // ending with a dot + `$`, // match end + ) ) // IsValidFqdn returns whether the given string is a valid fqdn. func IsValidFqdn(fqdn string) bool { - return cleanDomainRegex.MatchString(fqdn) + // root zone + if fqdn == "." { + return true + } + + // check max length + if len(fqdn) > 256 { + return false + } + + // check with regex + if !cleanDomainRegex.MatchString(fqdn) { + return false + } + + // check with miegk/dns + + // IsFqdn checks if a domain name is fully qualified. + if !dns.IsFqdn(fqdn) { + return false + } + + // IsDomainName checks if s is a valid domain name, it returns the number of + // labels and true, when a domain name is valid. Note that non fully qualified + // domain name is considered valid, in this case the last label is counted in + // the number of labels. When false is returned the number of labels is not + // defined. Also note that this function is extremely liberal; almost any + // string is a valid domain name as the DNS is 8 bit protocol. It checks if each + // label fits in 63 characters and that the entire name will fit into the 255 + // octet wire format limit. + _, ok := dns.IsDomainName(fqdn) + return ok } diff --git a/network/netutils/cleandns_test.go b/network/netutils/cleandns_test.go new file mode 100644 index 00000000..4f0dacb0 --- /dev/null +++ b/network/netutils/cleandns_test.go @@ -0,0 +1,43 @@ +package netutils + +import "testing" + +func testDomainValidity(t *testing.T, domain string, isValid bool) { + if IsValidFqdn(domain) != isValid { + t.Errorf("domain %s failed check: was valid=%v, expected valid=%v", domain, IsValidFqdn(domain), isValid) + } +} + +func TestDNSValidation(t *testing.T) { + // valid + testDomainValidity(t, ".", true) + testDomainValidity(t, "at.", true) + testDomainValidity(t, "orf.at.", true) + testDomainValidity(t, "www.orf.at.", true) + testDomainValidity(t, "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.x.y.z.example.org.", true) + testDomainValidity(t, "a_a.com.", true) + testDomainValidity(t, "a-a.com.", true) + testDomainValidity(t, "a_a.com.", true) + testDomainValidity(t, "a-a.com.", true) + testDomainValidity(t, "xn--a.com.", true) + testDomainValidity(t, "xn--asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasd.com.", true) + + // maybe valid + testDomainValidity(t, "-.com.", true) + testDomainValidity(t, "_.com.", true) + testDomainValidity(t, "a_.com.", true) + testDomainValidity(t, "a-.com.", true) + testDomainValidity(t, "_a.com.", true) + testDomainValidity(t, "-a.com.", true) + + // invalid + testDomainValidity(t, ".com.", false) + testDomainValidity(t, ".com.", false) + testDomainValidity(t, "xn--asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf.com.", false) + testDomainValidity(t, "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf.com.", false) + testDomainValidity(t, "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf.com.", false) + testDomainValidity(t, "asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.asdf.as.com.", false) + + // real world examples + testDomainValidity(t, "iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com.", true) +}