util/dnsname: make ToFQDN take exactly 0 or 1 allocs for everything.

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 <danderson@tailscale.com>
This commit is contained in:
David Anderson 2021-05-31 20:05:52 -07:00 committed by Dave Anderson
parent d7f6ef3a79
commit c54cc24e87

View File

@ -21,31 +21,48 @@
type FQDN string type FQDN string
func ToFQDN(s string) (FQDN, error) { func ToFQDN(s string) (FQDN, error) {
if isValidFQDN(s) {
return FQDN(s), nil
}
if len(s) == 0 || s == "." { if len(s) == 0 || s == "." {
return FQDN("."), nil return FQDN("."), nil
} }
if s[len(s)-1] == '.' {
s = s[:len(s)-1]
}
if s[0] == '.' { if s[0] == '.' {
s = s[1:] 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) return "", fmt.Errorf("%q is too long to be a DNS name", s)
} }
fs := strings.Split(s, ".") st := 0
for _, f := range fs { for i := 0; i < len(s); i++ {
if !validLabel(f) { if s[i] != '.' {
return "", fmt.Errorf("%q is not a valid DNS label", f) 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. // 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) 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 // SanitizeLabel takes a string intended to be a DNS name label
// and turns it into a valid name label according to RFC 1035. // and turns it into a valid name label according to RFC 1035.
func SanitizeLabel(label string) string { func SanitizeLabel(label string) string {