From f1e2bbc0e4418bbec2b3df8628a8e01a869327fa Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 18 Dec 2019 09:21:45 -0500 Subject: [PATCH] more permissive hostname validation In particular, allow hostnames without periods (like on a LAN). This shouldn't be a client compability concern since we allow vhosts without periods. --- irc/config.go | 2 +- irc/utils/net.go | 21 ++++++++++++--------- irc/utils/net_test.go | 17 +++++++++++++++-- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/irc/config.go b/irc/config.go index 09f1d588..464bcb00 100644 --- a/irc/config.go +++ b/irc/config.go @@ -593,7 +593,7 @@ func LoadConfig(filename string) (config *Config, err error) { if config.Server.Name == "" { return nil, ErrServerNameMissing } - if !utils.IsHostname(config.Server.Name) { + if !utils.IsServerName(config.Server.Name) { return nil, ErrServerNameNotHostname } if config.Datastore.Path == "" { diff --git a/irc/utils/net.go b/irc/utils/net.go index c5c98153..3eb13192 100644 --- a/irc/utils/net.go +++ b/irc/utils/net.go @@ -6,6 +6,7 @@ package utils import ( "net" + "regexp" "strings" ) @@ -13,6 +14,8 @@ var ( // subnet mask for an ipv6 /128: mask128 = net.CIDRMask(128, 128) IPv4LoopbackAddress = net.ParseIP("127.0.0.1").To16() + + validHostnameLabelRegexp = regexp.MustCompile(`^[0-9A-Za-z.\-]+$`) ) // AddrIsLocal returns whether the address is from a trusted local connection (loopback or unix). @@ -49,12 +52,10 @@ func IPStringToHostname(ipStr string) string { return ipStr } -var allowedHostnameChars = "abcdefghijklmnopqrstuvwxyz1234567890-." - // IsHostname returns whether we consider `name` a valid hostname. func IsHostname(name string) bool { - // IRC hostnames specifically require a period - if !strings.Contains(name, ".") || len(name) < 1 || len(name) > 253 { + name = strings.TrimSuffix(name, ".") + if len(name) < 1 || len(name) > 253 { return false } @@ -63,11 +64,7 @@ func IsHostname(name string) bool { if len(part) < 1 || len(part) > 63 || strings.HasPrefix(part, "-") || strings.HasSuffix(part, "-") { return false } - } - - // ensure all chars of hostname are valid - for _, char := range strings.Split(strings.ToLower(name), "") { - if !strings.Contains(allowedHostnameChars, char) { + if !validHostnameLabelRegexp.MatchString(part) { return false } } @@ -75,6 +72,12 @@ func IsHostname(name string) bool { return true } +// IsServerName returns whether we consider `name` a valid IRC server name. +func IsServerName(name string) bool { + // IRC server names specifically require a period + return IsHostname(name) && strings.IndexByte(name, '.') != -1 +} + // Convenience to test whether `ip` is contained in any of `nets`. func IPInNets(ip net.IP, nets []net.IPNet) bool { for _, network := range nets { diff --git a/irc/utils/net_test.go b/irc/utils/net_test.go index 0a8d595f..19a00bd1 100644 --- a/irc/utils/net_test.go +++ b/irc/utils/net_test.go @@ -24,14 +24,18 @@ var ( "gsf.ds342.co.uk", "324.net.uk", "xn--bcher-kva.ch", + "pentos", + "pentos.", + "www.google.com.", } badHostnames = []string{ "-lol-.net.uk", "-lol.net.uk", "_irc._sctp.lol.net.uk", - "irc", - "com", + "irc.l%l.net.uk", + "irc..net.uk", + ".", "", } ) @@ -56,6 +60,15 @@ func TestIsHostname(t *testing.T) { } } +func TestIsServerName(t *testing.T) { + if IsServerName("pentos") { + t.Error("irc server names must contain a period") + } + if !IsServerName("darwin.network") { + t.Error("failed to validate a perfectly good server name") + } +} + func TestNormalizeToNet(t *testing.T) { a := net.ParseIP("8.8.8.8") b := net.ParseIP("8.8.4.4")