mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-09-04 13:36:35 +02:00
87 lines
3.6 KiB
Go
87 lines
3.6 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// validHostname reports whether hostname is a valid hostname.
|
|
// A valid hostname contains 3 -> 64 characters and conform to RFC1123.
|
|
// This function validates hostnames to ensure they meet DNS naming standards
|
|
// and prevents invalid hostnames from being used in DNS configurations
|
|
func validHostname(hostname string) bool {
|
|
hostnameLen := len(hostname)
|
|
if hostnameLen < 3 || hostnameLen > 64 {
|
|
return false
|
|
}
|
|
// RFC1123 regex pattern ensures hostnames follow DNS naming conventions
|
|
// This prevents issues with DNS resolution and system compatibility
|
|
validHostnameRfc1123 := regexp.MustCompile(`^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$`)
|
|
return validHostnameRfc1123.MatchString(hostname)
|
|
}
|
|
|
|
// isHostnameChar reports whether r is part of validHostname's accepted
|
|
// charset (letters, digits, hyphen, dot). It does not check position, so a
|
|
// hostname can fail validHostname on structure (length, leading/trailing
|
|
// hyphen) while every one of its characters passes here.
|
|
func isHostnameChar(r rune) bool {
|
|
switch {
|
|
case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r >= '0' && r <= '9':
|
|
return true
|
|
case r == '-' || r == '.':
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// offendingHostnameChars returns the distinct characters in hostname that
|
|
// validHostname's charset does not accept, in first-seen order. Empty when
|
|
// every character is accepted - a rejection can still come from structure
|
|
// alone (too short, too long, a leading or trailing hyphen).
|
|
func offendingHostnameChars(hostname string) string {
|
|
seen := make(map[rune]bool)
|
|
var bad []rune
|
|
for _, r := range hostname {
|
|
if isHostnameChar(r) || seen[r] {
|
|
continue
|
|
}
|
|
seen[r] = true
|
|
bad = append(bad, r)
|
|
}
|
|
return string(bad)
|
|
}
|
|
|
|
// serverFoldedHostnameChars are the characters ControlD's
|
|
// DevicesTableModel.formatDeviceName folds to '-' (or strips) when it
|
|
// registers a device name server-side. Only '.' can actually reach
|
|
// hostnameMayBeFoldedByServer through validateCustomHostnameFlag's guarded
|
|
// path: validHostname runs first and already rejects any hostname
|
|
// containing a space or a '+' as CUSTOM_HOSTNAME_INVALID, so those two never
|
|
// get here from an explicit --custom-hostname value. They stay in this set
|
|
// for completeness: a mobile caller can set CustomHostname to an
|
|
// OS-derived default directly, without going through
|
|
// validateCustomHostnameFlag at all, so a space or '+' can still reach the
|
|
// API unvalidated by this client.
|
|
const serverFoldedHostnameChars = ". +"
|
|
|
|
// hostnameMayBeFoldedByServer reports whether hostname contains a character
|
|
// ControlD may fold or strip when it registers the device, so the name ctrld
|
|
// accepted may not be the name the dashboard ends up showing.
|
|
func hostnameMayBeFoldedByServer(hostname string) bool {
|
|
return strings.ContainsAny(hostname, serverFoldedHostnameChars)
|
|
}
|
|
|
|
// customHostnameFailureMessage names the field, the offending character(s)
|
|
// when there are any, and the allowed format for CUSTOM_HOSTNAME_INVALID.
|
|
// ctrld's accept/reject rule (validHostname) is unchanged - this only
|
|
// explains a rejection that used to be a bare fatal exit.
|
|
func customHostnameFailureMessage(hostname string) string {
|
|
const allowedFormat = "3-64 characters of letters, digits, hyphens, and dots (RFC1123 hostname format)"
|
|
reason := "is not a valid hostname"
|
|
if bad := offendingHostnameChars(hostname); bad != "" {
|
|
reason = fmt.Sprintf("contains characters a hostname cannot use: %q", bad)
|
|
}
|
|
return fmt.Sprintf("--custom-hostname (CustomHostname) %q %s; allowed format: %s", hostname, reason, allowedFormat)
|
|
}
|