This commit is contained in:
bluepython508 committed 2026-10-22 12:27:19 +01:00
1 parent 3530afb854
commit 6903fcff27
2146 files changed
+112256 -190703

No files matched your search

+44 -5
View File
@@ -1,4 +1,4 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
// Package dnsname contains string functions for working with DNS names.
@@ -20,6 +20,12 @@ const (
// A FQDN is a fully-qualified DNS name or name suffix.
type FQDN string
// ToFQDN returns s as an FQDN, with a trailing dot added if missing. The
// empty string and "." both parse as ".".
//
// Names containing whitespace or control characters are rejected. See the
// comment in the label loop below for why other characters are not
// validated.
func ToFQDN(s string) (FQDN, error) {
if len(s) == 0 || s == "." {
return FQDN("."), nil
@@ -39,6 +45,18 @@ func ToFQDN(s string) (FQDN, error) {
return "", vizerror.Errorf("%q is too long to be a DNS name", s)
}
// DNS labels on the wire may contain any non-zero byte (see the note in
// the label loop below about issue 2024), but names we accept from
// control are also written verbatim into line-oriented OS configuration
// files like resolv.conf and hosts, where a newline or carriage return
// can inject whole new directives. Reject whitespace and control
// characters so a malicious control server can't do that.
for i := range len(s) {
if c := s[i]; c <= ' ' || c == 0x7f {
return "", vizerror.Errorf("%q contains invalid character %q", raw, c)
}
}
st := 0
for i := range len(s) {
if s[i] != '.' {
@@ -94,14 +112,34 @@ func (f FQDN) Contains(other FQDN) bool {
return strings.HasSuffix(other.WithTrailingDot(), cmp)
}
// Parent returns the parent domain by stripping the first label.
// For "foo.bar.baz.", it returns "bar.baz."
// It returns an empty FQDN for root or single-label domains.
func (f FQDN) Parent() FQDN {
s := f.WithTrailingDot()
_, rest, ok := strings.Cut(s, ".")
if !ok || rest == "" {
return ""
}
return FQDN(rest)
}
// ValidLabel reports whether label is a valid DNS label. All errors are
// [vizerror.Error].
func ValidLabel(label string) error {
return ValidLabelLike(label, maxLabelLength)
}
// ValidLabelLike reports whether label contains DNS-valid characters
// only, adheres to a given maximum length, and starts and ends with
// a letter or number. For strict DNS-validity use [ValidLabel].
// All errors are [vizerror.Error].
func ValidLabelLike(label string, maxLen int) error {
if len(label) == 0 {
return vizerror.New("empty DNS label")
}
if len(label) > maxLabelLength {
return vizerror.Errorf("%q is too long, max length is %d bytes", label, maxLabelLength)
if len(label) > maxLen {
return vizerror.Errorf("%q is too long, max length is %d bytes", label, maxLen)
}
if !isalphanum(label[0]) {
return vizerror.Errorf("%q is not a valid DNS label: must start with a letter or number", label)
@@ -109,7 +147,8 @@ func ValidLabel(label string) error {
if !isalphanum(label[len(label)-1]) {
return vizerror.Errorf("%q is not a valid DNS label: must end with a letter or number", label)
}
if len(label) < 2 {
if len(label) <= 2 {
// Return early because we've already checked start and end characters (the only 2) are alphanumeric.
return nil
}
for i := 1; i < len(label)-1; i++ {
@@ -222,7 +261,7 @@ func ValidHostname(hostname string) error {
return err
}
for _, label := range strings.Split(fqdn.WithoutTrailingDot(), ".") {
for label := range strings.SplitSeq(fqdn.WithoutTrailingDot(), ".") {
if err := ValidLabel(label); err != nil {
return err
}