diff --git a/src/Atc/Extensions/StringHasIsExtensions.cs b/src/Atc/Extensions/StringHasIsExtensions.cs index ffb86e51..2987c795 100644 --- a/src/Atc/Extensions/StringHasIsExtensions.cs +++ b/src/Atc/Extensions/StringHasIsExtensions.cs @@ -598,11 +598,26 @@ public static bool IsUriOpcTcp(this string value) /// Accepts single-label names (e.g. localhost) and an optional trailing dot (e.g. example.com.). /// Each label is 1-63 ASCII alphanumeric/hyphen characters and may not start or end with a hyphen; /// the total length is limited to 253 characters. Underscores and raw Unicode (non-punycode IDN) are not allowed. + /// Per RFC 1123 §2.1 the top-level (right-most) label must not be all-numeric, which also disambiguates + /// host names from IPv4 addresses (e.g. 192.168.0.27 is rejected). /// This is a purely syntactic check and does not perform any DNS resolution. /// public static bool IsHostName(this string value) - => !string.IsNullOrEmpty(value) && - RxHostName.Value.IsMatch(value); + { + if (string.IsNullOrEmpty(value) || + !RxHostName.Value.IsMatch(value)) + { + return false; + } + + var host = value.EndsWith('.') + ? value[..^1] + : value; + + var topLevelLabel = host[(host.LastIndexOf('.') + 1)..]; + + return !topLevelLabel.All(char.IsDigit); + } /// /// Determines whether the specified value is a valid IPv4 address. diff --git a/test/Atc.Tests/Extensions/StringHasIsExtensionsTests.cs b/test/Atc.Tests/Extensions/StringHasIsExtensionsTests.cs index 25b07587..0e29789c 100644 --- a/test/Atc.Tests/Extensions/StringHasIsExtensionsTests.cs +++ b/test/Atc.Tests/Extensions/StringHasIsExtensionsTests.cs @@ -384,6 +384,7 @@ public void IsUriOpcTcp( [InlineData(true, "example.com.")] [InlineData(true, "a.b.c.d.e.f")] [InlineData(true, "xn--mnchen-3ya.de")] + [InlineData(true, "host123")] [InlineData(false, "")] [InlineData(false, " ")] [InlineData(false, "-leadinghyphen.com")] @@ -392,6 +393,10 @@ public void IsUriOpcTcp( [InlineData(false, "double..dot.com")] [InlineData(false, "space in.host")] [InlineData(false, "münchen.de")] + [InlineData(false, "192.168.0.27")] + [InlineData(false, "1.2.3.4")] + [InlineData(false, "123")] + [InlineData(false, "example.123")] public void IsHostName( bool expected, string input)