From c54cc24e878d53d5edaf25aebfc05cec0e15e650 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 31 May 2021 20:05:52 -0700 Subject: [PATCH] util/dnsname: make ToFQDN take exactly 0 or 1 allocs for everything. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit name old time/op new time/op delta ToFQDN/www.tailscale.com.-32 9.55ns ± 2% 12.13ns ± 3% +27.03% (p=0.000 n=10+10) ToFQDN/www.tailscale.com-32 86.3ns ± 1% 40.7ns ± 1% -52.86% (p=0.000 n=10+9) ToFQDN/.www.tailscale.com-32 86.5ns ± 1% 40.4ns ± 1% -53.29% (p=0.000 n=10+9) ToFQDN/_ssh._tcp.www.tailscale.com.-32 12.8ns ± 2% 14.7ns ± 2% +14.24% (p=0.000 n=9+10) ToFQDN/_ssh._tcp.www.tailscale.com-32 104ns ± 1% 45ns ± 0% -57.16% (p=0.000 n=10+9) name old alloc/op new alloc/op delta ToFQDN/www.tailscale.com.-32 0.00B 0.00B ~ (all equal) ToFQDN/www.tailscale.com-32 72.0B ± 0% 24.0B ± 0% -66.67% (p=0.000 n=10+10) ToFQDN/.www.tailscale.com-32 72.0B ± 0% 24.0B ± 0% -66.67% (p=0.000 n=10+10) ToFQDN/_ssh._tcp.www.tailscale.com.-32 0.00B 0.00B ~ (all equal) ToFQDN/_ssh._tcp.www.tailscale.com-32 112B ± 0% 32B ± 0% -71.43% (p=0.000 n=10+10) name old allocs/op new allocs/op delta ToFQDN/www.tailscale.com.-32 0.00 0.00 ~ (all equal) ToFQDN/www.tailscale.com-32 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=10+10) ToFQDN/.www.tailscale.com-32 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=10+10) ToFQDN/_ssh._tcp.www.tailscale.com.-32 0.00 0.00 ~ (all equal) ToFQDN/_ssh._tcp.www.tailscale.com-32 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=10+10) Signed-off-by: David Anderson --- util/dnsname/dnsname.go | 93 +++++++++++++---------------------------- 1 file changed, 29 insertions(+), 64 deletions(-) diff --git a/util/dnsname/dnsname.go b/util/dnsname/dnsname.go index 6ed85c84f..667abfe0e 100644 --- a/util/dnsname/dnsname.go +++ b/util/dnsname/dnsname.go @@ -21,31 +21,48 @@ const ( type FQDN string func ToFQDN(s string) (FQDN, error) { - if isValidFQDN(s) { - return FQDN(s), nil - } if len(s) == 0 || s == "." { return FQDN("."), nil } - if s[len(s)-1] == '.' { - s = s[:len(s)-1] - } if s[0] == '.' { s = s[1:] } - if len(s) > maxNameLength { + raw := s + totalLen := len(s) + if s[len(s)-1] == '.' { + s = s[:len(s)-1] + } else { + totalLen += 1 // account for missing dot + } + if totalLen > maxNameLength { return "", fmt.Errorf("%q is too long to be a DNS name", s) } - fs := strings.Split(s, ".") - for _, f := range fs { - if !validLabel(f) { - return "", fmt.Errorf("%q is not a valid DNS label", f) + st := 0 + for i := 0; i < len(s); i++ { + if s[i] != '.' { + continue } + label := s[st:i] + // You might be tempted to do further validation of the + // contents of labels here, based on the hostname rules in RFC + // 1123. However, DNS labels are not always subject to + // hostname rules. In general, they can contain any non-zero + // byte sequence, even though in practice a more restricted + // set is used. + // + // See https://github.com/tailscale/tailscale/issues/2024 for more. + if len(label) == 0 || len(label) > maxLabelLength { + return "", fmt.Errorf("%q is not a valid DNS label", label) + } + st = i + 1 } - return FQDN(s + "."), nil + if raw[len(raw)-1] != '.' { + raw = raw + "." + } + return FQDN(raw), nil } // WithTrailingDot returns f as a string, with a trailing dot. @@ -77,58 +94,6 @@ func (f FQDN) Contains(other FQDN) bool { return strings.HasSuffix(other.WithTrailingDot(), cmp) } -// isValidFQDN reports whether s is already a valid FQDN, without -// allocating. -func isValidFQDN(s string) bool { - if len(s) == 0 { - return false - } - if len(s) > maxNameLength { - return false - } - // DNS root name. - if s == "." { - return true - } - // Missing trailing dot. - if s[len(s)-1] != '.' { - return false - } - // Leading dots not allowed. - if s[0] == '.' { - return false - } - - st := 0 - for i := 0; i < len(s); i++ { - if s[i] != '.' { - continue - } - label := s[st:i] - if !validLabel(label) { - return false - } - st = i + 1 - } - - return true -} - -func validLabel(s string) bool { - // You might be tempted to do further validation of the - // contents of labels here, based on the hostname rules in RFC - // 1123. However, DNS labels are not always subject to - // hostname rules. In general, they can contain any non-zero - // byte sequence, even though in practice a more restricted - // set is used. - // - // See https://github.com/tailscale/tailscale/issues/2024 for more. - if len(s) == 0 || len(s) > maxLabelLength { - return false - } - return true -} - // SanitizeLabel takes a string intended to be a DNS name label // and turns it into a valid name label according to RFC 1035. func SanitizeLabel(label string) string {