diff --git a/Source/GlobalAssemblyInfo.cs b/Source/GlobalAssemblyInfo.cs index 1b58f5b727..f756fd756f 100644 --- a/Source/GlobalAssemblyInfo.cs +++ b/Source/GlobalAssemblyInfo.cs @@ -6,5 +6,5 @@ [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2025.11.16.0")] -[assembly: AssemblyFileVersion("2025.11.16.0")] +[assembly: AssemblyVersion("2025.12.10.0")] +[assembly: AssemblyFileVersion("2025.12.10.0")] diff --git a/Source/NETworkManager.Converters/DNSServerConnectionInfoProfileToString.cs b/Source/NETworkManager.Converters/DNSServerConnectionInfoProfileToString.cs deleted file mode 100644 index feb47ef7db..0000000000 --- a/Source/NETworkManager.Converters/DNSServerConnectionInfoProfileToString.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; -using NETworkManager.Localization.Resources; -using NETworkManager.Models.Network; - -namespace NETworkManager.Converters; - -public sealed class DNSServerConnectionInfoProfileToString : IValueConverter -{ - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is not DNSServerConnectionInfoProfile dnsServerInfo) - return "-/-"; - - return dnsServerInfo.UseWindowsDNSServer ? $"[{Strings.WindowsDNSSettings}]" : dnsServerInfo.Name; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/Source/NETworkManager.Converters/StringIsNotNullOrEmptyOrIPv4AddressToBooleanConverter.cs b/Source/NETworkManager.Converters/StringIsNotNullOrEmptyOrIPv4AddressToBooleanConverter.cs index 31e3e5a3d8..a118a33030 100644 --- a/Source/NETworkManager.Converters/StringIsNotNullOrEmptyOrIPv4AddressToBooleanConverter.cs +++ b/Source/NETworkManager.Converters/StringIsNotNullOrEmptyOrIPv4AddressToBooleanConverter.cs @@ -1,8 +1,7 @@ -using System; +using NETworkManager.Utilities; +using System; using System.Globalization; -using System.Text.RegularExpressions; using System.Windows.Data; -using NETworkManager.Utilities; namespace NETworkManager.Converters; @@ -10,7 +9,7 @@ public sealed class StringIsNotNullOrEmptyOrIPv4AddressToBooleanConverter : IVal { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - return !string.IsNullOrEmpty(value as string) && !Regex.IsMatch((string)value, RegexHelper.IPv4AddressRegex); + return !string.IsNullOrEmpty(value as string) && !RegexHelper.IPv4AddressRegex().IsMatch((string)value); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/Source/NETworkManager.Converters/ValidateSubnetCalculatorSubnettingConverter.cs b/Source/NETworkManager.Converters/ValidateSubnetCalculatorSubnettingConverter.cs index 3e97213917..3b5eccf929 100644 --- a/Source/NETworkManager.Converters/ValidateSubnetCalculatorSubnettingConverter.cs +++ b/Source/NETworkManager.Converters/ValidateSubnetCalculatorSubnettingConverter.cs @@ -2,7 +2,6 @@ using System.Globalization; using System.Net; using System.Net.Sockets; -using System.Text.RegularExpressions; using System.Windows.Data; using NETworkManager.Models.Network; using NETworkManager.Utilities; @@ -40,7 +39,7 @@ public object Convert(object[] values, Type targetType, object parameter, Cultur }; // Support subnetmask like 255.255.255.0 - int newCidr = Regex.IsMatch(newSubnetmaskOrCidr, RegexHelper.SubnetmaskRegex) + int newCidr = RegexHelper.SubnetmaskRegex().IsMatch(newSubnetmaskOrCidr) ? System.Convert.ToByte(Subnetmask.ConvertSubnetmaskToCidr(IPAddress.Parse(newSubnetmaskOrCidr))) : System.Convert.ToByte(newSubnetmaskOrCidr.TrimStart('/')); diff --git a/Source/NETworkManager.Documentation/NETworkManager.Documentation.csproj b/Source/NETworkManager.Documentation/NETworkManager.Documentation.csproj index f344bd7af9..996f839fac 100644 --- a/Source/NETworkManager.Documentation/NETworkManager.Documentation.csproj +++ b/Source/NETworkManager.Documentation/NETworkManager.Documentation.csproj @@ -1,4 +1,4 @@ - + {95CE4AD5-14C0-4486-9C11-5D6A5EC48176} Library diff --git a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs index 7e2dea087e..ca27acd2f3 100644 --- a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs +++ b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs @@ -2157,6 +2157,15 @@ public static string CouldNotGetPublicIPAddressFromXXXMessage { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die Could not parse or resolve any of the specified DNS servers. ähnelt. + /// + public static string CouldNotParseOrResolveDNSServers { + get { + return ResourceManager.GetString("CouldNotParseOrResolveDNSServers", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die Could not parse public ip address from "{0}"! Try another service or use the default... ähnelt. /// @@ -2166,6 +2175,15 @@ public static string CouldNotParsePublicIPAddressFromXXXMessage { } } + /// + /// Sucht eine lokalisierte Zeichenfolge, die Could not parse DNS server "{0}". ähnelt. + /// + public static string CouldNotParseX { + get { + return ResourceManager.GetString("CouldNotParseX", resourceCulture); + } + } + /// /// Sucht eine lokalisierte Zeichenfolge, die Could not resolve hostname for: "{0}" ähnelt. /// diff --git a/Source/NETworkManager.Localization/Resources/Strings.resx b/Source/NETworkManager.Localization/Resources/Strings.resx index cad95c179a..c72d6e06b5 100644 --- a/Source/NETworkManager.Localization/Resources/Strings.resx +++ b/Source/NETworkManager.Localization/Resources/Strings.resx @@ -3945,4 +3945,10 @@ If you click Cancel, the profile file will remain unencrypted. A restart is required to apply changes such as language settings. + + Could not parse or resolve any of the specified DNS servers. + + + Could not parse DNS server "{0}". + \ No newline at end of file diff --git a/Source/NETworkManager.Models/Network/DNSLookupErrorArgs.cs b/Source/NETworkManager.Models/Network/DNSLookupErrorArgs.cs index ee773b65ca..59d93615d0 100644 --- a/Source/NETworkManager.Models/Network/DNSLookupErrorArgs.cs +++ b/Source/NETworkManager.Models/Network/DNSLookupErrorArgs.cs @@ -6,6 +6,7 @@ public class DNSLookupErrorArgs : EventArgs { public DNSLookupErrorArgs() { + } public DNSLookupErrorArgs(string query, string server, string ipEndPoint, string errorMessage) diff --git a/Source/NETworkManager.Models/Network/DNSServer.cs b/Source/NETworkManager.Models/Network/DNSServer.cs index e654d973cb..acbd0270ee 100644 --- a/Source/NETworkManager.Models/Network/DNSServer.cs +++ b/Source/NETworkManager.Models/Network/DNSServer.cs @@ -13,34 +13,39 @@ public static class DNSServer /// List of common dns servers. public static List GetDefaultList() { - return new List - { + return + [ new(), // Windows DNS server - new("Cloudflare", new List - { + new("Cloudflare", + [ new("1.1.1.1", 53, TransportProtocol.Udp), new("1.0.0.1", 53, TransportProtocol.Udp) - }), - new("DNS.Watch", new List - { + ]), + new("DNS.Watch", + [ new("84.200.69.80", 53, TransportProtocol.Udp), new("84.200.70.40", 53, TransportProtocol.Udp) - }), - new("Google Public DNS", new List - { + ]), + new("Google Public DNS", + [ new("8.8.8.8", 53, TransportProtocol.Udp), new("8.8.4.4", 53, TransportProtocol.Udp) - }), - new("Level3", new List - { + ]), + new("Level3", + [ new("209.244.0.3", 53, TransportProtocol.Udp), new("209.244.0.4", 53, TransportProtocol.Udp) - }), - new("Verisign", new List - { + ]), + new("Quad9", + [ + new ("9.9.9.9", 53, TransportProtocol.Udp), + new ("149.112.112.112", 53 , TransportProtocol.Udp) + ]), + new("Verisign", + [ new("64.6.64.6", 53, TransportProtocol.Udp), new("64.6.65.6", 53, TransportProtocol.Udp) - }) - }; + ]) + ]; } } \ No newline at end of file diff --git a/Source/NETworkManager.Models/Network/DNSServerConnectionInfoProfile.cs b/Source/NETworkManager.Models/Network/DNSServerConnectionInfoProfile.cs index 7e06301e30..ccc9209aae 100644 --- a/Source/NETworkManager.Models/Network/DNSServerConnectionInfoProfile.cs +++ b/Source/NETworkManager.Models/Network/DNSServerConnectionInfoProfile.cs @@ -13,6 +13,7 @@ public class DNSServerConnectionInfoProfile : ServerConnectionInfoProfile /// public DNSServerConnectionInfoProfile() { + Name = "[Windows DNS]"; UseWindowsDNSServer = true; } @@ -23,6 +24,7 @@ public DNSServerConnectionInfoProfile() /// List of servers as . public DNSServerConnectionInfoProfile(string name, List servers) : base(name, servers) { + } /// diff --git a/Source/NETworkManager.Models/Network/HostRangeHelper.cs b/Source/NETworkManager.Models/Network/HostRangeHelper.cs index 9017e5ce80..43ccdc505b 100644 --- a/Source/NETworkManager.Models/Network/HostRangeHelper.cs +++ b/Source/NETworkManager.Models/Network/HostRangeHelper.cs @@ -1,4 +1,5 @@ -using System.Collections.Concurrent; +using NETworkManager.Utilities; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net; @@ -6,7 +7,6 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using NETworkManager.Utilities; namespace NETworkManager.Models.Network; @@ -46,7 +46,7 @@ private static (List<(IPAddress ipAddress, string hostname)> hosts, List switch (host) { // 192.168.0.1 - case var _ when Regex.IsMatch(host, RegexHelper.IPv4AddressRegex): + case var _ when RegexHelper.IPv4AddressRegex().IsMatch(host): // 2001:db8:85a3::8a2e:370:7334 case var _ when Regex.IsMatch(host, RegexHelper.IPv6AddressRegex): hostsBag.Add((IPAddress.Parse(host), string.Empty)); @@ -54,9 +54,9 @@ private static (List<(IPAddress ipAddress, string hostname)> hosts, List break; // 192.168.0.0/24 - case var _ when Regex.IsMatch(host, RegexHelper.IPv4AddressCidrRegex): + case var _ when RegexHelper.IPv4AddressCidrRegex().IsMatch(host): // 192.168.0.0/255.255.255.0 - case var _ when Regex.IsMatch(host, RegexHelper.IPv4AddressSubnetmaskRegex): + case var _ when RegexHelper.IPv4AddressSubnetmaskRegex().IsMatch(host): var network = IPNetwork2.Parse(host); Parallel.For(IPv4Address.ToInt32(network.Network), IPv4Address.ToInt32(network.Broadcast) + 1, @@ -71,7 +71,7 @@ private static (List<(IPAddress ipAddress, string hostname)> hosts, List break; // 192.168.0.0 - 192.168.0.100 - case var _ when Regex.IsMatch(host, RegexHelper.IPv4AddressRangeRegex): + case var _ when RegexHelper.IPv4AddressRangeRegex().IsMatch(host): var range = host.Split('-'); Parallel.For(IPv4Address.ToInt32(IPAddress.Parse(range[0])), @@ -86,7 +86,7 @@ private static (List<(IPAddress ipAddress, string hostname)> hosts, List break; // 192.168.[50-100].1 - case var _ when Regex.IsMatch(host, RegexHelper.IPv4AddressSpecialRangeRegex): + case var _ when RegexHelper.IPv4AddressSpecialRangeRegex().IsMatch(host): var octets = host.Split('.'); var list = new List>(); @@ -147,7 +147,7 @@ private static (List<(IPAddress ipAddress, string hostname)> hosts, List break; // example.com - case var _ when Regex.IsMatch(host, RegexHelper.HostnameOrDomainRegex): + case var _ when RegexHelper.HostnameOrDomainRegex().IsMatch(host): using (var dnsResolverTask = DNSClientHelper.ResolveAorAaaaAsync(host, dnsResolveHostnamePreferIPv4)) { diff --git a/Source/NETworkManager.Models/Network/SNTPLookup.cs b/Source/NETworkManager.Models/Network/SNTPLookup.cs index 6f6b8d351c..0a20f369c0 100644 --- a/Source/NETworkManager.Models/Network/SNTPLookup.cs +++ b/Source/NETworkManager.Models/Network/SNTPLookup.cs @@ -1,10 +1,11 @@ -using System; +using ControlzEx.Standard; +using NETworkManager.Utilities; +using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text.RegularExpressions; using System.Threading.Tasks; -using NETworkManager.Utilities; namespace NETworkManager.Models.Network; @@ -102,7 +103,7 @@ public void QueryAsync(IEnumerable servers, bool dnsResolv // NTP requires an IP address to connect to IPAddress serverIP = null; - if (Regex.IsMatch(server.Server, RegexHelper.IPv4AddressRegex) || + if (RegexHelper.IPv4AddressRegex().IsMatch(server.Server) || Regex.IsMatch(server.Server, RegexHelper.IPv6AddressRegex)) { serverIP = IPAddress.Parse(server.Server); diff --git a/Source/NETworkManager.Models/Network/ServerConnectionInfo.cs b/Source/NETworkManager.Models/Network/ServerConnectionInfo.cs index b36491c7b3..0a402771a6 100644 --- a/Source/NETworkManager.Models/Network/ServerConnectionInfo.cs +++ b/Source/NETworkManager.Models/Network/ServerConnectionInfo.cs @@ -1,4 +1,11 @@ -namespace NETworkManager.Models.Network; +using NETworkManager.Utilities; +using System; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Sockets; + +namespace NETworkManager.Models.Network; /// /// Class contains information about a server. @@ -10,6 +17,7 @@ public class ServerConnectionInfo /// public ServerConnectionInfo() { + } /// @@ -52,6 +60,111 @@ public ServerConnectionInfo(string server, int port, TransportProtocol transport /// public TransportProtocol TransportProtocol { get; set; } + /// + /// Tries to parse a server connection string into a object. + /// Supports formats: IPv4, IPv4:port, IPv6, [IPv6], [IPv6]:port, hostname, hostname:port + /// + /// Server connection string to parse. + /// Parsed object if successful. + /// Default port to use if not specified in input. + /// Transport protocol to set in the parsed object. + /// True if parsing was successful; otherwise, false. + public static bool TryParse(string input, out ServerConnectionInfo serverConnectionInfo, int defaultPort, TransportProtocol transportProtocol) + { + serverConnectionInfo = null; + + if (string.IsNullOrWhiteSpace(input)) + return false; + + input = input.Trim(); + + Debug.WriteLine("Parse server connection info input: " + input); + + // [IPv6]:Port + if (input.StartsWith('[') && input.Contains("]:")) + { + var endIndex = input.IndexOf("]:", StringComparison.Ordinal); + var ipPart = input[1..endIndex]; + var portPart = input[(endIndex + 2)..]; + + Debug.WriteLine($"IPv6 with port detected. IP: {ipPart}, Port: {portPart}"); + + if (IPAddress.TryParse(ipPart, out IPAddress ip) && ip.AddressFamily == AddressFamily.InterNetworkV6 && + int.TryParse(portPart, out int port) && port > 0 && port <= 65535) + { + serverConnectionInfo = new ServerConnectionInfo(ip.ToString(), port, transportProtocol); + return true; + } + } + // [IPv6] + else if (input.StartsWith('[') && input.EndsWith(']')) + { + var ipPart = input[1..^1]; + + Debug.WriteLine($"IPv6 without port detected. IP: {ipPart}"); + + if (IPAddress.TryParse(ipPart, out IPAddress ip) && ip.AddressFamily == AddressFamily.InterNetworkV6) + { + serverConnectionInfo = new ServerConnectionInfo(ip.ToString(), defaultPort, transportProtocol); + return true; + } + } + // IPv6 without brackets (contains multiple colons) + else if (input.Count(c => c == ':') > 1) + { + Debug.WriteLine($"IPv6 without port detected. IP: {input}"); + + if (IPAddress.TryParse(input, out IPAddress ip) && ip.AddressFamily == AddressFamily.InterNetworkV6) + { + serverConnectionInfo = new ServerConnectionInfo(ip.ToString(), defaultPort, transportProtocol); + return true; + } + } + // IPv4/hostname:port (single colon) + else if (input.Contains(':')) + { + var parts = input.Split([':'], 2); + + Debug.WriteLine($"IPv4 or Hostname with port detected. Host: {parts[0]}, Port: {parts[1]}"); + + if ((RegexHelper.IPv4AddressRegex().IsMatch(parts[0]) || RegexHelper.HostnameOrDomainRegex().IsMatch(parts[0])) && + int.TryParse(parts[1], out int port) && port > 0 && port <= 65535) + { + serverConnectionInfo = new ServerConnectionInfo(parts[0], port, transportProtocol); + return true; + } + } + // IPv4/hostname + else + { + if (RegexHelper.IPv4AddressRegex().IsMatch(input) || RegexHelper.HostnameOrDomainRegex().IsMatch(input)) + { + Debug.WriteLine($"IPv4 or Hostname without port detected. Host: {input}"); + + serverConnectionInfo = new ServerConnectionInfo(input, defaultPort, transportProtocol); + return true; + } + } + + return false; + } + + /// + /// Parses a server connection string into a object. + /// Supports formats: IPv4, IPv4:port, IPv6, [IPv6], [IPv6]:port, hostname, hostname:port + /// + /// Server connection string to parse. + /// Default port to use if not specified in input. + /// Parsed object. + /// Thrown when the input string is not in a valid format. + public static ServerConnectionInfo Parse(string input, int defaultPort, TransportProtocol transportProtocol) + { + if (TryParse(input, out var serverConnectionInfo, defaultPort, transportProtocol)) + return serverConnectionInfo; + + throw new FormatException($"Could not parse server connection info from input: {input}"); + } + /// /// Returns a string that represents the current object. /// diff --git a/Source/NETworkManager.Models/Network/ServerConnectionInfoProfile.cs b/Source/NETworkManager.Models/Network/ServerConnectionInfoProfile.cs index 8d4848d83c..a50f03ddbc 100644 --- a/Source/NETworkManager.Models/Network/ServerConnectionInfoProfile.cs +++ b/Source/NETworkManager.Models/Network/ServerConnectionInfoProfile.cs @@ -12,6 +12,7 @@ public class ServerConnectionInfoProfile /// public ServerConnectionInfoProfile() { + } /// @@ -33,5 +34,10 @@ public ServerConnectionInfoProfile(string name, List serve /// /// List of servers as . /// - public List Servers { get; set; } = new(); + public List Servers { get; set; } = []; + + public override string ToString() + { + return Name; + } } \ No newline at end of file diff --git a/Source/NETworkManager.Settings/SettingsInfo.cs b/Source/NETworkManager.Settings/SettingsInfo.cs index a60d8bcf01..7f195085d2 100644 --- a/Source/NETworkManager.Settings/SettingsInfo.cs +++ b/Source/NETworkManager.Settings/SettingsInfo.cs @@ -1723,7 +1723,7 @@ public ExportFileType Traceroute_ExportFileType #region DNS Lookup - private ObservableCollection _dnsLookup_HostHistory = new(); + private ObservableCollection _dnsLookup_HostHistory = []; public ObservableCollection DNSLookup_HostHistory { @@ -1738,7 +1738,7 @@ public ObservableCollection DNSLookup_HostHistory } } - private ObservableCollection _dnsLookup_DNSServers = new(); + private ObservableCollection _dnsLookup_DNSServers = []; public ObservableCollection DNSLookup_DNSServers { @@ -1753,8 +1753,10 @@ public ObservableCollection DNSLookup_DNSServers } } - private DNSServerConnectionInfoProfile _dnsLookup_SelectedDNSServer = new(); + [Obsolete("Use DNSLookup_SelectedDNSServer_v2 instead.")] + private DNSServerConnectionInfoProfile _dnsLookup_SelectedDNSServer = null; + [Obsolete("Use DNSLookup_SelectedDNSServer_v2 instead.")] public DNSServerConnectionInfoProfile DNSLookup_SelectedDNSServer { get => _dnsLookup_SelectedDNSServer; @@ -1768,37 +1770,35 @@ public DNSServerConnectionInfoProfile DNSLookup_SelectedDNSServer } } - private QueryClass _dnsLookup_QueryClass = GlobalStaticConfiguration.DNSLookup_QueryClass; + private string _dnsLookup_SelectedDNSServer_v2; - public QueryClass DNSLookup_QueryClass + public string DNSLookup_SelectedDNSServer_v2 { - get => _dnsLookup_QueryClass; + get => _dnsLookup_SelectedDNSServer_v2; set { - if (value == _dnsLookup_QueryClass) + if (value == _dnsLookup_SelectedDNSServer_v2) return; - _dnsLookup_QueryClass = value; + _dnsLookup_SelectedDNSServer_v2 = value; OnPropertyChanged(); } } - /* - private bool _dnsLookup_ShowOnlyMostCommonQueryTypes = true; + private QueryClass _dnsLookup_QueryClass = GlobalStaticConfiguration.DNSLookup_QueryClass; - public bool DNSLookup_ShowOnlyMostCommonQueryTypes + public QueryClass DNSLookup_QueryClass { - get => _dnsLookup_ShowOnlyMostCommonQueryTypes; + get => _dnsLookup_QueryClass; set { - if (value == _dnsLookup_ShowOnlyMostCommonQueryTypes) + if (value == _dnsLookup_QueryClass) return; - _dnsLookup_ShowOnlyMostCommonQueryTypes = value; + _dnsLookup_QueryClass = value; OnPropertyChanged(); } } - */ private QueryType _dnsLookup_QueryType = GlobalStaticConfiguration.DNSLookup_QueryType; diff --git a/Source/NETworkManager.Settings/SettingsManager.cs b/Source/NETworkManager.Settings/SettingsManager.cs index 2d3146aab9..cbd20d5ff4 100644 --- a/Source/NETworkManager.Settings/SettingsManager.cs +++ b/Source/NETworkManager.Settings/SettingsManager.cs @@ -337,6 +337,16 @@ private static void UpgradeToLatest(Version version) { Log.Info($"Apply upgrade to {version}..."); + // DNS Lookup + + Log.Info("Migrate DNS Lookup settings to new structure..."); + + Current.DNSLookup_SelectedDNSServer_v2 = Current.DNSLookup_SelectedDNSServer?.Name; + + Log.Info($"Selected DNS server set to \"{Current.DNSLookup_SelectedDNSServer_v2}\""); + + // AWS Session Manager + Log.Info("Removing deprecated app \"AWS Session Manager\", if it exists..."); var appToRemove = Current.General_ApplicationList diff --git a/Source/NETworkManager.Utilities/RegexHelper.cs b/Source/NETworkManager.Utilities/RegexHelper.cs index c1445c70a7..ec5d2385a1 100644 --- a/Source/NETworkManager.Utilities/RegexHelper.cs +++ b/Source/NETworkManager.Utilities/RegexHelper.cs @@ -1,29 +1,89 @@ -namespace NETworkManager.Utilities; +using System.Text.RegularExpressions; -public static class RegexHelper +namespace NETworkManager.Utilities; + +public static partial class RegexHelper { /// - /// Match an IPv4-Address like 192.168.178.1 - /// - // ReSharper disable once InconsistentNaming + /// Represents a regular expression pattern that matches valid IPv4 address values. + /// private const string IPv4AddressValues = @"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"; /// - /// Match exactly an IPv4-Address like 192.168.178.1 + /// Represents a regular expression pattern that matches valid IPv4 subnet mask values. + /// + private const string SubnetmaskValues = + @"(((255\.){3}(255|254|252|248|240|224|192|128|0+))|((255\.){2}(255|254|252|248|240|224|192|128|0+)\.0)|((255\.)(255|254|252|248|240|224|192|128|0+)(\.0+){2})|((255|254|252|248|240|224|192|128|0+)(\.0+){3}))"; + + /// + /// Represents the regular expression pattern used to validate CIDR notation values for IPv4 subnet masks. + /// + private const string CidrRegexValues = @"([1-9]|[1-2][0-9]|3[0-2])"; + + /// + /// Represents a regular expression pattern that matches valid hostnames or fully qualified domain names (FQDNs). /// - // ReSharper disable once InconsistentNaming - public const string IPv4AddressRegex = $"^{IPv4AddressValues}$"; + private const string HostnameOrDomainValues = + @"(?=.{1,255}$)(?!-)[A-Za-z0-9-]{1,63}(? + /// Provides a compiled regular expression that matches valid IPv4 addresses in dot-decimal notation like "192.168.178.0". + /// + /// A instance that matches IPv4 addresses in the format "x.x.x.x", where each x is a number + /// from 0 to 255. + [GeneratedRegex($"^{IPv4AddressValues}$")] + public static partial Regex IPv4AddressRegex(); + + /// + /// Provides a compiled regular expression that matches valid IPv4 addresses within input text like "192.168.178.0". + /// + /// A instance that can be used to extract IPv4 addresses from strings. + [GeneratedRegex(IPv4AddressValues)] + public static partial Regex IPv4AddressExtractRegex(); + + /// + /// Provides a compiles regular expression that matches IPv4 address ranges in the format "start-end" like + /// "192.168.178.0-192.168.178.255". + /// + /// A instance that matches strings representing IPv4 address ranges, such as + /// "192.168.1.1-192.168.1.100". + [GeneratedRegex($"^{IPv4AddressValues}-{IPv4AddressValues}$")] + public static partial Regex IPv4AddressRangeRegex(); + + /// + /// Provides a compiled regular expression that matches valid IPv4 subnet mask like "255.255.0.0". + /// + /// A instance that matches strings representing valid IPv4 subnet masks. + [GeneratedRegex($"^{SubnetmaskValues}$")] + public static partial Regex SubnetmaskRegex(); /// - /// Match IPv4-Address within a string + /// Provides a compiled regular expression that matches IPv4 addresses with subnet masks like + /// "192.168.178.0/255.255.255.0". + /// + /// A instance that matches strings containing an IPv4 address followed by a subnet mask, + /// separated by a forward slash. + [GeneratedRegex($@"^{IPv4AddressValues}\/{SubnetmaskValues}$")] + public static partial Regex IPv4AddressSubnetmaskRegex(); + + /// + /// Provides a compiled regular expression that matches an IPv4 address with CIDR like + /// "192.168.178.0/24". /// - // ReSharper disable once InconsistentNaming - public const string IPv4AddressExtractRegex = IPv4AddressValues; + /// A instance that matches strings containing an IPv4 address followed by a slash and a valid + /// CIDR prefix length. + [GeneratedRegex($@"^{IPv4AddressValues}\/{CidrRegexValues}$")] + public static partial Regex IPv4AddressCidrRegex(); - // Match IPv4-Address Range like 192.168.178.1-192.168.178.127 - // ReSharper disable once InconsistentNaming - public const string IPv4AddressRangeRegex = $"^{IPv4AddressValues}-{IPv4AddressValues}$"; + /// + /// Provides a compiled regular expression that matches IPv4 addresses, allowing for a special range in one or more octets like + /// "192.168.[0-50].1". + /// + /// A instance that matches IPv4 addresses with support for custom special ranges like + /// "192.168.[0-50].1". + [GeneratedRegex($@"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|{SpecialRangeRegex})\.){{3}}((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|{SpecialRangeRegex})$")] + public static partial Regex IPv4AddressSpecialRangeRegex(); // Match a MAC-Address 000000000000 00:00:00:00:00:00, 00-00-00-00-00-00-00 or 0000.0000.0000 public const string MACAddressRegex = @@ -33,23 +93,6 @@ public static class RegexHelper public const string MACAddressFirst3BytesRegex = @"^[A-Fa-f0-9]{6}$|^[A-Fa-f0-9]{2}(:|-){1}[A-Fa-f0-9]{2}(:|-){1}[A-Fa-f0-9]{2}$|^[A-Fa-f0-9]{4}.[A-Fa-f0-9]{2}$"; - // Private subnetmask / cidr values - private const string SubnetmaskValues = - @"(((255\.){3}(255|254|252|248|240|224|192|128|0+))|((255\.){2}(255|254|252|248|240|224|192|128|0+)\.0)|((255\.)(255|254|252|248|240|224|192|128|0+)(\.0+){2})|((255|254|252|248|240|224|192|128|0+)(\.0+){3}))"; - - private const string CidrRegex = @"([1-9]|[1-2][0-9]|3[0-2])"; - - // Match a Subnetmask like 255.255.255.0 - public const string SubnetmaskRegex = @"^" + SubnetmaskValues + @"$"; - - // Match a subnet from 192.168.178.0/1 to 192.168.178.0/32 - // ReSharper disable once InconsistentNaming - public const string IPv4AddressCidrRegex = $@"^{IPv4AddressValues}\/{CidrRegex}$"; - - // Match a subnet from 192.168.178.0/192.0.0.0 to 192.168.178.0/255.255.255.255 - // ReSharper disable once InconsistentNaming - public const string IPv4AddressSubnetmaskRegex = $@"^{IPv4AddressValues}\/{SubnetmaskValues}$"; - // Match IPv6 address like ::1 // ReSharper disable once InconsistentNaming public const string IPv6AddressRegex = @@ -64,20 +107,18 @@ public static class RegexHelper public const string SpecialRangeRegex = @"\[((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)-(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))([,]((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)-(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))))*\]"; - // Match a IPv4-Address like 192.168.[50-100].1 - // ReSharper disable once InconsistentNaming - public const string IPv4AddressSpecialRangeRegex = - $@"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|{SpecialRangeRegex})\.){{3}}((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|{SpecialRangeRegex})$"; - // Private hostname values - private const string HostnameOrDomainValues = - @"(?=.{1,255}$)(?!-)[A-Za-z0-9-]{1,63}(? + /// Provides a compiled regular expression that matches valid hostnames or fully qualified domain names (FQDNs) like + /// server-01 or server-01.example.com. + /// + /// A instance that matches valid hostnames or FQDNs. + [GeneratedRegex($@"^{HostnameOrDomainValues}$")] + public static partial Regex HostnameOrDomainRegex(); // Match a hostname with cidr like server-01.example.com/24 - public const string HostnameOrDomainWithCidrRegex = $@"^{HostnameOrDomainValues}\/{CidrRegex}$"; + public const string HostnameOrDomainWithCidrRegex = $@"^{HostnameOrDomainValues}\/{CidrRegexValues}$"; // Match a hostname with subnetmask like server-01.example.com/255.255.255.0 public const string HostnameOrDomainWithSubnetmaskRegex = $@"^{HostnameOrDomainValues}\/{SubnetmaskValues}$"; diff --git a/Source/NETworkManager.Validators/EmptyOrIPv4AddressValidator.cs b/Source/NETworkManager.Validators/EmptyOrIPv4AddressValidator.cs index 471a02fc5a..53acea2a99 100644 --- a/Source/NETworkManager.Validators/EmptyOrIPv4AddressValidator.cs +++ b/Source/NETworkManager.Validators/EmptyOrIPv4AddressValidator.cs @@ -1,8 +1,9 @@ -using System.Globalization; -using System.Text.RegularExpressions; -using System.Windows.Controls; +using ControlzEx.Standard; using NETworkManager.Localization.Resources; using NETworkManager.Utilities; +using System.Globalization; +using System.Text.RegularExpressions; +using System.Windows.Controls; namespace NETworkManager.Validators; @@ -13,7 +14,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) if (string.IsNullOrEmpty(value as string)) return ValidationResult.ValidResult; - return Regex.IsMatch((string)value, RegexHelper.IPv4AddressRegex) + return RegexHelper.IPv4AddressRegex().IsMatch((string)value) ? ValidationResult.ValidResult : new ValidationResult(false, Strings.EnterValidIPv4Address); } diff --git a/Source/NETworkManager.Validators/EmptyOrWindowsDomainValidator.cs b/Source/NETworkManager.Validators/EmptyOrWindowsDomainValidator.cs index 380736f27d..33217399a2 100644 --- a/Source/NETworkManager.Validators/EmptyOrWindowsDomainValidator.cs +++ b/Source/NETworkManager.Validators/EmptyOrWindowsDomainValidator.cs @@ -1,5 +1,4 @@ using System.Globalization; -using System.Text.RegularExpressions; using System.Windows.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Utilities; @@ -19,7 +18,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) if (domain.Equals(".")) return ValidationResult.ValidResult; - return Regex.IsMatch(domain, RegexHelper.HostnameOrDomainRegex) + return RegexHelper.HostnameOrDomainRegex().IsMatch(domain) ? ValidationResult.ValidResult : new ValidationResult(false, Strings.EnterValidDomain); } diff --git a/Source/NETworkManager.Validators/HostsFileEntryHostnameValidator.cs b/Source/NETworkManager.Validators/HostsFileEntryHostnameValidator.cs index f8e1ab397f..d99c9700bf 100644 --- a/Source/NETworkManager.Validators/HostsFileEntryHostnameValidator.cs +++ b/Source/NETworkManager.Validators/HostsFileEntryHostnameValidator.cs @@ -2,7 +2,6 @@ using NETworkManager.Utilities; using System.Diagnostics; using System.Globalization; -using System.Text.RegularExpressions; using System.Windows.Controls; namespace NETworkManager.Validators; @@ -19,7 +18,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) foreach (var hostname in input.Split(' ')) { - if (Regex.IsMatch(hostname, RegexHelper.HostnameOrDomainRegex) == false) + if (RegexHelper.HostnameOrDomainRegex().IsMatch(hostname) == false) { isValid = false; break; diff --git a/Source/NETworkManager.Validators/IPAddressOrHostnameAsRangeValidator.cs b/Source/NETworkManager.Validators/IPAddressOrHostnameAsRangeValidator.cs index 8392c450dc..86b9efd9a6 100644 --- a/Source/NETworkManager.Validators/IPAddressOrHostnameAsRangeValidator.cs +++ b/Source/NETworkManager.Validators/IPAddressOrHostnameAsRangeValidator.cs @@ -1,8 +1,9 @@ -using System.Globalization; +using NETworkManager.Localization.Resources; +using NETworkManager.Utilities; +using System.DirectoryServices.ActiveDirectory; +using System.Globalization; using System.Text.RegularExpressions; using System.Windows.Controls; -using NETworkManager.Localization.Resources; -using NETworkManager.Utilities; namespace NETworkManager.Validators; @@ -20,9 +21,9 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) var localItem = item.Trim(); // Check if it is a valid IPv4 address like 192.168.0.1, a valid IPv6 address like ::1 or a valid hostname like server-01 or server-01.example.com - var isValid = Regex.IsMatch(localItem, RegexHelper.IPv4AddressRegex) || + var isValid = RegexHelper.IPv4AddressRegex().IsMatch(localItem) || Regex.IsMatch(localItem, RegexHelper.IPv6AddressRegex) || - Regex.IsMatch(localItem, RegexHelper.HostnameOrDomainRegex); + RegexHelper.HostnameOrDomainRegex().IsMatch(localItem); if (!isValid) return new ValidationResult(false, Strings.EnterValidHostnameOrIPAddress); diff --git a/Source/NETworkManager.Validators/IPAddressOrHostnameValidator.cs b/Source/NETworkManager.Validators/IPAddressOrHostnameValidator.cs index 3aa1980789..40ad7d72c8 100644 --- a/Source/NETworkManager.Validators/IPAddressOrHostnameValidator.cs +++ b/Source/NETworkManager.Validators/IPAddressOrHostnameValidator.cs @@ -1,8 +1,9 @@ -using System.Globalization; +using NETworkManager.Localization.Resources; +using NETworkManager.Utilities; +using System.DirectoryServices.ActiveDirectory; +using System.Globalization; using System.Text.RegularExpressions; using System.Windows.Controls; -using NETworkManager.Localization.Resources; -using NETworkManager.Utilities; namespace NETworkManager.Validators; @@ -16,7 +17,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) return new ValidationResult(false, Strings.EnterValidHostnameOrIPAddress); // Check if it is a valid IPv4 address like 192.168.0.1 - if (Regex.IsMatch(input, RegexHelper.IPv4AddressRegex)) + if (RegexHelper.IPv4AddressRegex().IsMatch(input)) return ValidationResult.ValidResult; // Check if it is a valid IPv6 address like ::1 @@ -24,7 +25,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) return ValidationResult.ValidResult; // Check if it is a valid hostname like server-01 or server-01.example.com - if (Regex.IsMatch(input, RegexHelper.HostnameOrDomainRegex)) + if (RegexHelper.HostnameOrDomainRegex().IsMatch(input)) return ValidationResult.ValidResult; return new ValidationResult(false, Strings.EnterValidHostnameOrIPAddress); diff --git a/Source/NETworkManager.Validators/IPv4AddressValidator.cs b/Source/NETworkManager.Validators/IPv4AddressValidator.cs index 9149f54977..2ed8c3060a 100644 --- a/Source/NETworkManager.Validators/IPv4AddressValidator.cs +++ b/Source/NETworkManager.Validators/IPv4AddressValidator.cs @@ -1,8 +1,7 @@ -using System.Globalization; -using System.Text.RegularExpressions; -using System.Windows.Controls; -using NETworkManager.Localization.Resources; +using NETworkManager.Localization.Resources; using NETworkManager.Utilities; +using System.Globalization; +using System.Windows.Controls; namespace NETworkManager.Validators; @@ -15,7 +14,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) if (string.IsNullOrEmpty(ipAddress)) return new ValidationResult(false, Strings.EnterValidIPv4Address); - return Regex.IsMatch(ipAddress, RegexHelper.IPv4AddressRegex) + return RegexHelper.IPv4AddressRegex().IsMatch(ipAddress) ? ValidationResult.ValidResult : new ValidationResult(false, Strings.EnterValidIPv4Address); } diff --git a/Source/NETworkManager.Validators/IPv4IPv6SubnetValidator.cs b/Source/NETworkManager.Validators/IPv4IPv6SubnetValidator.cs index 7902a7385d..2da5665ec6 100644 --- a/Source/NETworkManager.Validators/IPv4IPv6SubnetValidator.cs +++ b/Source/NETworkManager.Validators/IPv4IPv6SubnetValidator.cs @@ -16,11 +16,11 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) return new ValidationResult(false, Strings.EnterValidSubnet); // Check if it is a IPv4 address with a cidr like 192.168.0.0/24 - if (Regex.IsMatch(subnet, RegexHelper.IPv4AddressCidrRegex)) + if (RegexHelper.IPv4AddressCidrRegex().IsMatch(subnet)) return ValidationResult.ValidResult; // Check if it is a IPv4 address with a subnetmask like 255.255.255.0 - if (Regex.IsMatch(subnet, RegexHelper.IPv4AddressSubnetmaskRegex)) + if (RegexHelper.IPv4AddressSubnetmaskRegex().IsMatch(subnet)) return ValidationResult.ValidResult; // check if it is a IPv6 address with a cidr like ::1/64 diff --git a/Source/NETworkManager.Validators/IPv4IPv6SubnetmaskOrCIDRValidator.cs b/Source/NETworkManager.Validators/IPv4IPv6SubnetmaskOrCIDRValidator.cs index 465c2cc41e..769efc1e9b 100644 --- a/Source/NETworkManager.Validators/IPv4IPv6SubnetmaskOrCIDRValidator.cs +++ b/Source/NETworkManager.Validators/IPv4IPv6SubnetmaskOrCIDRValidator.cs @@ -1,5 +1,4 @@ using System.Globalization; -using System.Text.RegularExpressions; using System.Windows.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Utilities; @@ -16,7 +15,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) return new ValidationResult(false, Strings.EnterValidSubnetmaskOrCIDR); // Check if it is a subnetmask like 255.255.255.0 - if (Regex.IsMatch(subnetmaskOrCidr, RegexHelper.SubnetmaskRegex)) + if (RegexHelper.SubnetmaskRegex().IsMatch(subnetmaskOrCidr)) return ValidationResult.ValidResult; // Check if it is a CIDR like /24 diff --git a/Source/NETworkManager.Validators/IPv4SubnetValidator.cs b/Source/NETworkManager.Validators/IPv4SubnetValidator.cs index 3186a2a58c..5927b8f22e 100644 --- a/Source/NETworkManager.Validators/IPv4SubnetValidator.cs +++ b/Source/NETworkManager.Validators/IPv4SubnetValidator.cs @@ -1,5 +1,4 @@ using System.Globalization; -using System.Text.RegularExpressions; using System.Windows.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Utilities; @@ -15,10 +14,10 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) if (string.IsNullOrEmpty(subnet)) return new ValidationResult(false, Strings.EnterValidSubnet); - if (Regex.IsMatch(subnet, RegexHelper.IPv4AddressCidrRegex)) + if (RegexHelper.IPv4AddressCidrRegex().IsMatch(subnet)) return ValidationResult.ValidResult; - if (Regex.IsMatch(subnet, RegexHelper.IPv4AddressSubnetmaskRegex)) + if (RegexHelper.IPv4AddressSubnetmaskRegex().IsMatch(subnet)) return ValidationResult.ValidResult; return new ValidationResult(false, Strings.EnterValidSubnet); diff --git a/Source/NETworkManager.Validators/IPv4SubnetmaskOrCIDRValidator.cs b/Source/NETworkManager.Validators/IPv4SubnetmaskOrCIDRValidator.cs index 80f47780fb..a189e89179 100644 --- a/Source/NETworkManager.Validators/IPv4SubnetmaskOrCIDRValidator.cs +++ b/Source/NETworkManager.Validators/IPv4SubnetmaskOrCIDRValidator.cs @@ -1,5 +1,4 @@ using System.Globalization; -using System.Text.RegularExpressions; using System.Windows.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Utilities; @@ -16,7 +15,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) return new ValidationResult(false, Strings.EnterValidSubnetmaskOrCIDR); - if (Regex.IsMatch(subnetmaskOrCidr, RegexHelper.SubnetmaskRegex)) + if (RegexHelper.SubnetmaskRegex().IsMatch(subnetmaskOrCidr)) return ValidationResult.ValidResult; if (int.TryParse(subnetmaskOrCidr.TrimStart('/'), out var cidr)) diff --git a/Source/NETworkManager.Validators/MultipleHostsRangeValidator.cs b/Source/NETworkManager.Validators/MultipleHostsRangeValidator.cs index aa6f4bba96..a2674ce8ae 100644 --- a/Source/NETworkManager.Validators/MultipleHostsRangeValidator.cs +++ b/Source/NETworkManager.Validators/MultipleHostsRangeValidator.cs @@ -1,10 +1,11 @@ -using System.Globalization; +using NETworkManager.Localization.Resources; +using NETworkManager.Models.Network; +using NETworkManager.Utilities; +using System.DirectoryServices.ActiveDirectory; +using System.Globalization; using System.Net; using System.Text.RegularExpressions; using System.Windows.Controls; -using NETworkManager.Localization.Resources; -using NETworkManager.Models.Network; -using NETworkManager.Utilities; namespace NETworkManager.Validators; @@ -20,19 +21,19 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) foreach (var ipHostOrRange in ((string)value).Replace(" ", "").Split(';')) { // 192.168.0.1 - if (Regex.IsMatch(ipHostOrRange, RegexHelper.IPv4AddressRegex)) + if (RegexHelper.IPv4AddressRegex().IsMatch(ipHostOrRange)) continue; // 192.168.0.0/24 - if (Regex.IsMatch(ipHostOrRange, RegexHelper.IPv4AddressCidrRegex)) + if (RegexHelper.IPv4AddressCidrRegex().IsMatch(ipHostOrRange)) continue; // 192.168.0.0/255.255.255.0 - if (Regex.IsMatch(ipHostOrRange, RegexHelper.IPv4AddressSubnetmaskRegex)) + if (RegexHelper.IPv4AddressSubnetmaskRegex().IsMatch(ipHostOrRange)) continue; // 192.168.0.0 - 192.168.0.100 - if (Regex.IsMatch(ipHostOrRange, RegexHelper.IPv4AddressRangeRegex)) + if (RegexHelper.IPv4AddressRangeRegex().IsMatch(ipHostOrRange)) { var range = ipHostOrRange.Split('-'); @@ -43,7 +44,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) } // 192.168.[50-100].1 - if (Regex.IsMatch(ipHostOrRange, RegexHelper.IPv4AddressSpecialRangeRegex)) + if (RegexHelper.IPv4AddressSpecialRangeRegex().IsMatch(ipHostOrRange)) { var octets = ipHostOrRange.Split('.'); @@ -74,7 +75,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) continue; // server-01.example.com - if (Regex.IsMatch(ipHostOrRange, RegexHelper.HostnameOrDomainRegex)) + if (RegexHelper.HostnameOrDomainRegex().IsMatch(ipHostOrRange)) continue; // server-01.example.com/24 diff --git a/Source/NETworkManager.Validators/MultipleIPAddressesValidator.cs b/Source/NETworkManager.Validators/MultipleIPAddressesValidator.cs index 9089606473..ed0d1a3d83 100644 --- a/Source/NETworkManager.Validators/MultipleIPAddressesValidator.cs +++ b/Source/NETworkManager.Validators/MultipleIPAddressesValidator.cs @@ -1,8 +1,8 @@ -using System.Globalization; +using NETworkManager.Localization.Resources; +using NETworkManager.Utilities; +using System.Globalization; using System.Text.RegularExpressions; using System.Windows.Controls; -using NETworkManager.Localization.Resources; -using NETworkManager.Utilities; namespace NETworkManager.Validators; @@ -15,13 +15,13 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) for (var index = 0; index < ((string)value).Split(';').Length; index++) { - var ipAddress = ((string)value).Split(';')[index]; + var ipAddress = ((string)value).Split(';')[index].Trim(); - if (!Regex.IsMatch(ipAddress.Trim(), RegexHelper.IPv4AddressRegex) && + if (!RegexHelper.IPv4AddressRegex().IsMatch(ipAddress) && !Regex.IsMatch(ipAddress.Trim(), RegexHelper.IPv6AddressRegex)) return new ValidationResult(false, Strings.EnterOneOrMoreValidIPAddresses); } return ValidationResult.ValidResult; } -} \ No newline at end of file +} diff --git a/Source/NETworkManager.Validators/RemoteDesktopHostnameAndPortValidator.cs b/Source/NETworkManager.Validators/RemoteDesktopHostnameAndPortValidator.cs index 880de518fd..32b78db059 100644 --- a/Source/NETworkManager.Validators/RemoteDesktopHostnameAndPortValidator.cs +++ b/Source/NETworkManager.Validators/RemoteDesktopHostnameAndPortValidator.cs @@ -1,8 +1,9 @@ -using System.Globalization; +using NETworkManager.Localization.Resources; +using NETworkManager.Utilities; +using System.DirectoryServices.ActiveDirectory; +using System.Globalization; using System.Text.RegularExpressions; using System.Windows.Controls; -using NETworkManager.Localization.Resources; -using NETworkManager.Utilities; namespace NETworkManager.Validators; @@ -16,7 +17,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) { var hostnameAndPortValues = hostnameAndPort.Split(':'); - if (Regex.IsMatch(hostnameAndPortValues[0], RegexHelper.HostnameOrDomainRegex) && + if (RegexHelper.HostnameOrDomainRegex().IsMatch(hostnameAndPortValues[0]) && !string.IsNullOrEmpty(hostnameAndPortValues[1]) && Regex.IsMatch(hostnameAndPortValues[1], RegexHelper.PortRegex)) return ValidationResult.ValidResult; @@ -24,7 +25,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) return new ValidationResult(false, Strings.EnterValidHostnameAndPort); } - return Regex.IsMatch((string)value, RegexHelper.HostnameOrDomainRegex) + return RegexHelper.HostnameOrDomainRegex().IsMatch((string)value) ? ValidationResult.ValidResult : new ValidationResult(false, Strings.EnterValidHostname); } diff --git a/Source/NETworkManager.Validators/ServerValidator.cs b/Source/NETworkManager.Validators/ServerValidator.cs index bd82d4f88c..bdd0416db3 100644 --- a/Source/NETworkManager.Validators/ServerValidator.cs +++ b/Source/NETworkManager.Validators/ServerValidator.cs @@ -6,23 +6,25 @@ namespace NETworkManager.Validators; -public class ServerValidator : ValidationRule +public partial class ServerValidator : ValidationRule { public ServerDependencyObjectWrapper Wrapper { get; set; } public override ValidationResult Validate(object value, CultureInfo cultureInfo) { var allowOnlyIPAddress = Wrapper.AllowOnlyIPAddress; + var genericErrorResult = allowOnlyIPAddress ? Strings.EnterValidIPAddress : Strings.EnterValidHostnameOrIPAddress; var input = (value as string)?.Trim(); + // Empty input is considered invalid if (string.IsNullOrEmpty(input)) return new ValidationResult(false, genericErrorResult); // Check if it is a valid IPv4 address like 192.168.0.1 - if (Regex.IsMatch(input, RegexHelper.IPv4AddressRegex)) + if (RegexHelper.IPv4AddressRegex().IsMatch(input)) return ValidationResult.ValidResult; // Check if it is a valid IPv6 address like ::1 @@ -30,7 +32,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) return ValidationResult.ValidResult; // Check if it is a valid hostname like server-01 or server-01.example.com - if (Regex.IsMatch(input, RegexHelper.HostnameOrDomainRegex) && !allowOnlyIPAddress) + if (RegexHelper.HostnameOrDomainRegex().IsMatch(input) && !allowOnlyIPAddress) return ValidationResult.ValidResult; return new ValidationResult(false, genericErrorResult); diff --git a/Source/NETworkManager.Validators/SubnetmaskValidator.cs b/Source/NETworkManager.Validators/SubnetmaskValidator.cs index f12a6adfa6..044c3da1e7 100644 --- a/Source/NETworkManager.Validators/SubnetmaskValidator.cs +++ b/Source/NETworkManager.Validators/SubnetmaskValidator.cs @@ -1,5 +1,4 @@ using System.Globalization; -using System.Text.RegularExpressions; using System.Windows.Controls; using NETworkManager.Localization.Resources; using NETworkManager.Utilities; @@ -10,7 +9,7 @@ public class SubnetmaskValidator : ValidationRule { public override ValidationResult Validate(object value, CultureInfo cultureInfo) { - return value != null && Regex.IsMatch((string)value, RegexHelper.SubnetmaskRegex) + return value != null && RegexHelper.SubnetmaskRegex().IsMatch((string)value) ? ValidationResult.ValidResult : new ValidationResult(false, Strings.EnterValidSubnetmask); } diff --git a/Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs b/Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs index da95fab145..7db27bdec4 100644 --- a/Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs @@ -586,7 +586,7 @@ private void CollapseAllProfileGroupsAction() { SetIsExpandedForAllProfileGroups(false); } - + /// /// Gets the callback for closing a tab item. /// @@ -613,7 +613,7 @@ private void SetIsExpandedForAllProfileGroups(bool isExpanded) foreach (var group in Profiles.Groups.Cast()) GroupExpanderStateStore[group.Name.ToString()] = isExpanded; } - + /// /// Resizes the profile view. /// diff --git a/Source/NETworkManager/ViewModels/DNSLookupSettingsViewModel.cs b/Source/NETworkManager/ViewModels/DNSLookupSettingsViewModel.cs index 6cb9a0f035..be62bedbcf 100644 --- a/Source/NETworkManager/ViewModels/DNSLookupSettingsViewModel.cs +++ b/Source/NETworkManager/ViewModels/DNSLookupSettingsViewModel.cs @@ -367,7 +367,7 @@ private void LoadSettings() } #endregion - + #region ICommand & Actions /// @@ -418,27 +418,30 @@ private void DeleteDNSServerAction() /// private async Task AddDNSServer() { - var customDialog = new CustomDialog - { - Title = Strings.AddDNSServer - }; + var childWindow = new ServerConnectionInfoProfileChildWindow(); - var viewModel = new ServerConnectionInfoProfileViewModel(instance => - { - _dialogCoordinator.HideMetroDialogAsync(this, customDialog); + var childWindowViewModel = new ServerConnectionInfoProfileViewModel(instance => + { + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; - SettingsManager.Current.DNSLookup_DNSServers.Add( - new DNSServerConnectionInfoProfile(instance.Name, instance.Servers.ToList())); - }, _ => { _dialogCoordinator.HideMetroDialogAsync(this, customDialog); }, - (ServerInfoProfileNames, false, true), + SettingsManager.Current.DNSLookup_DNSServers.Add( + new DNSServerConnectionInfoProfile(instance.Name, [.. instance.Servers])); + }, _ => + { + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; + }, + (ServerInfoProfileNames, false, false), _profileDialogDefaultValues); - customDialog.Content = new ServerConnectionInfoProfileDialog - { - DataContext = viewModel - }; + childWindow.Title = Strings.AddDNSServer; + + childWindow.DataContext = childWindowViewModel; + + ConfigurationManager.Current.IsChildWindowOpen = true; - await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog); + await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow); } /// @@ -446,28 +449,31 @@ private async Task AddDNSServer() /// public async Task EditDNSServer() { - var customDialog = new CustomDialog - { - Title = Strings.EditDNSServer - }; + var childWindow = new ServerConnectionInfoProfileChildWindow(); - var viewModel = new ServerConnectionInfoProfileViewModel(instance => - { - _dialogCoordinator.HideMetroDialogAsync(this, customDialog); + var childWindowViewModel = new ServerConnectionInfoProfileViewModel(instance => + { + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; - SettingsManager.Current.DNSLookup_DNSServers.Remove(SelectedDNSServer); - SettingsManager.Current.DNSLookup_DNSServers.Add( - new DNSServerConnectionInfoProfile(instance.Name, instance.Servers.ToList())); - }, _ => { _dialogCoordinator.HideMetroDialogAsync(this, customDialog); }, - (ServerInfoProfileNames, true, true), + SettingsManager.Current.DNSLookup_DNSServers.Remove(SelectedDNSServer); + SettingsManager.Current.DNSLookup_DNSServers.Add( + new DNSServerConnectionInfoProfile(instance.Name, [.. instance.Servers])); + }, _ => + { + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; + }, + (ServerInfoProfileNames, true, false), _profileDialogDefaultValues, SelectedDNSServer); - customDialog.Content = new ServerConnectionInfoProfileDialog - { - DataContext = viewModel - }; + childWindow.Title = Strings.EditDNSServer; + + childWindow.DataContext = childWindowViewModel; + + ConfigurationManager.Current.IsChildWindowOpen = true; - await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog); + await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow); } /// diff --git a/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs b/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs index 36a0e99795..0924da7dd2 100644 --- a/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs +++ b/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs @@ -15,7 +15,9 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Diagnostics; using System.Linq; +using System.Net; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; @@ -80,31 +82,50 @@ public string Host /// /// Backing field for . /// - private DNSServerConnectionInfoProfile _dnsServer = new(); + private string _dnsServer; /// /// Gets or sets the selected DNS server. + /// This can either be an ip/host:port or a profile name. /// - public DNSServerConnectionInfoProfile DNSServer + public string DNSServer { get => _dnsServer; set { - if (value == _dnsServer) + if (_dnsServer == value) return; + // Try finding matching dns server profile by name, otherwise set to null (de-select) + SelectedDNSServer = SettingsManager.Current.DNSLookup_DNSServers + .FirstOrDefault(x => x.Name == value); + if (!_isLoading) - SettingsManager.Current.DNSLookup_SelectedDNSServer = value; + SettingsManager.Current.DNSLookup_SelectedDNSServer_v2 = value; _dnsServer = value; OnPropertyChanged(); } } + private DNSServerConnectionInfoProfile _selectedDNSServer; + public DNSServerConnectionInfoProfile SelectedDNSServer + { + get => _selectedDNSServer; + set + { + if (_selectedDNSServer == value) + return; + + _selectedDNSServer = value; + OnPropertyChanged(); + } + } + /// /// Backing field for . /// - private List _queryTypes = new(); + private List _queryTypes = []; /// /// Gets the list of available query types. @@ -170,7 +191,7 @@ public bool IsRunning /// /// Backing field for . /// - private ObservableCollection _results = new(); + private ObservableCollection _results = []; /// /// Gets or sets the collection of lookup results. @@ -304,9 +325,10 @@ public DNSLookupViewModel(IDialogCoordinator instance, Guid tabId, string host) ListSortDirection.Descending)); DNSServers.SortDescriptions.Add(new SortDescription(nameof(DNSServerConnectionInfoProfile.Name), ListSortDirection.Ascending)); - DNSServer = DNSServers.SourceCollection.Cast() - .FirstOrDefault(x => x.Name == SettingsManager.Current.DNSLookup_SelectedDNSServer.Name) ?? - DNSServers.SourceCollection.Cast().First(); + + DNSServer = string.IsNullOrEmpty(SettingsManager.Current.DNSLookup_SelectedDNSServer_v2) + ? SettingsManager.Current.DNSLookup_DNSServers.FirstOrDefault()?.Name + : SettingsManager.Current.DNSLookup_SelectedDNSServer_v2; ResultsView = CollectionViewSource.GetDefaultView(Results); ResultsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(DNSLookupRecordInfo.NameServerAsString))); @@ -330,7 +352,7 @@ public void OnLoaded() return; if (!string.IsNullOrEmpty(Host)) - Query(); + QueryAsync().ConfigureAwait(false); _firstLoad = false; } @@ -390,7 +412,7 @@ private bool Query_CanExecute(object parameter) private void QueryAction() { if (!IsRunning) - Query(); + QueryAsync().ConfigureAwait(false); } /// @@ -409,18 +431,16 @@ private void ExportAction() #endregion #region Methods - /// /// Performs the DNS query. /// - private void Query() + private async Task QueryAsync() { IsStatusMessageDisplayed = false; StatusMessage = string.Empty; IsRunning = true; - // Reset the latest results Results.Clear(); DragablzTabItem.SetTabHeader(_tabId, Host); @@ -445,9 +465,82 @@ private void Query() dnsSettings.CustomDNSSuffix = SettingsManager.Current.DNSLookup_CustomDNSSuffix?.TrimStart('.'); } - var dnsLookup = DNSServer.UseWindowsDNSServer - ? new DNSLookup(dnsSettings) - : new DNSLookup(dnsSettings, DNSServer.Servers); + // Try to find DNS server profile + var dnsServerProfile = SettingsManager.Current.DNSLookup_DNSServers + .FirstOrDefault(x => x.Name == DNSServer); + + DNSLookup dnsLookup = null; + List dnsServersToResolve = []; + + // Use DNS server profile if found + if (dnsServerProfile != null) + { + if (dnsServerProfile.UseWindowsDNSServer) + dnsLookup = new DNSLookup(dnsSettings); + else + dnsServersToResolve = dnsServerProfile.Servers; + } + // Parse DNS server input + else + { + foreach (var dnsServerInput in DNSServer.Split(';')) + { + if (ServerConnectionInfo.TryParse(dnsServerInput, out var serverInfo, 53, TransportProtocol.Udp)) + dnsServersToResolve.Add(serverInfo); + else + AppendStatusMessage(Strings.DNSServer + ": " + string.Format(Strings.CouldNotParseX, dnsServerInput)); + } + } + + // Resolve DNS server hostnames (if any) - not required when using Windows DNS servers + if (dnsLookup == null) + { + List resolvedDNSServers = []; + + foreach (var dnsServerToResolve in dnsServersToResolve) + { + // Check if already an IP address + if (IPAddress.TryParse(dnsServerToResolve.Server, out _)) + { + resolvedDNSServers.Add(dnsServerToResolve); + + continue; + } + + // Resolve hostname to IP address + var dnsResult = await DNSClientHelper.ResolveAorAaaaAsync(dnsServerToResolve.Server, + SettingsManager.Current.Network_ResolveHostnamePreferIPv4); + + if (dnsResult.HasError) + { + var dnsErrorMessage = DNSClientHelper.FormatDNSClientResultError(dnsServerToResolve.Server, dnsResult); + + AppendStatusMessage($"{Strings.DNSServer}: {dnsErrorMessage}"); + + continue; + } + + resolvedDNSServers.Add(new ServerConnectionInfo( + dnsResult.Value.ToString(), + dnsServerToResolve.Port, + dnsServerToResolve.TransportProtocol) + ); + } + + // Create DNS lookup instance + if (resolvedDNSServers.Count > 0) + { + dnsLookup = new DNSLookup(dnsSettings, resolvedDNSServers); + } + else + { + AppendStatusMessage(Strings.CouldNotParseOrResolveDNSServers); + + IsRunning = false; + + return; + } + } dnsLookup.RecordReceived += DNSLookup_RecordReceived; dnsLookup.LookupError += DNSLookup_LookupError; @@ -470,6 +563,19 @@ public void OnClose() ConfigurationManager.Current.DNSLookupTabCount--; } + /// + /// Appends the specified message to the current status message, adding a new line if a message already exists. + /// + /// The status message text to append. Cannot be null. + private void AppendStatusMessage(string message) + { + if (!string.IsNullOrEmpty(StatusMessage)) + StatusMessage += Environment.NewLine; + + StatusMessage += message; + IsStatusMessageDisplayed = true; + } + // Modify history list /// /// Adds the host to the history. @@ -478,7 +584,7 @@ public void OnClose() private void AddHostToHistory(string host) { // Create the new list - var list = ListHelper.Modify(SettingsManager.Current.DNSLookup_HostHistory.ToList(), host, + var list = ListHelper.Modify([.. SettingsManager.Current.DNSLookup_HostHistory], host, SettingsManager.Current.General_HistoryListEntries); // Clear the old items diff --git a/Source/NETworkManager/ViewModels/HostsFileEditorViewModel.cs b/Source/NETworkManager/ViewModels/HostsFileEditorViewModel.cs index cb4937d19a..08f77b591b 100644 --- a/Source/NETworkManager/ViewModels/HostsFileEditorViewModel.cs +++ b/Source/NETworkManager/ViewModels/HostsFileEditorViewModel.cs @@ -429,7 +429,7 @@ private async Task AddEntryAction() { childWindow.IsOpen = false; ConfigurationManager.Current.IsChildWindowOpen = false; - + IsModifying = false; }); diff --git a/Source/NETworkManager/ViewModels/IPGeolocationHostViewModel.cs b/Source/NETworkManager/ViewModels/IPGeolocationHostViewModel.cs index 7f4fdc19f0..bdea0a12fb 100644 --- a/Source/NETworkManager/ViewModels/IPGeolocationHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/IPGeolocationHostViewModel.cs @@ -586,7 +586,7 @@ private void CollapseAllProfileGroupsAction() { SetIsExpandedForAllProfileGroups(false); } - + /// /// Gets the callback for closing a tab item. /// @@ -613,7 +613,7 @@ private void SetIsExpandedForAllProfileGroups(bool isExpanded) foreach (var group in Profiles.Groups.Cast()) GroupExpanderStateStore[group.Name.ToString()] = isExpanded; } - + /// /// Resizes the profile view. /// diff --git a/Source/NETworkManager/ViewModels/IPScannerHostViewModel.cs b/Source/NETworkManager/ViewModels/IPScannerHostViewModel.cs index 6952823984..995d91872c 100644 --- a/Source/NETworkManager/ViewModels/IPScannerHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/IPScannerHostViewModel.cs @@ -560,7 +560,7 @@ private void ClearProfileFilterAction() IsProfileFilterSet = false; ProfileFilterIsOpen = false; } - + /// /// Gets the command to expand all profile groups. /// @@ -613,7 +613,7 @@ private void SetIsExpandedForAllProfileGroups(bool isExpanded) foreach (var group in Profiles.Groups.Cast()) GroupExpanderStateStore[group.Name.ToString()] = isExpanded; } - + /// /// Resizes the profile view. /// diff --git a/Source/NETworkManager/ViewModels/LookupOUILookupViewModel.cs b/Source/NETworkManager/ViewModels/LookupOUILookupViewModel.cs index 1b3c31c54e..76daee1589 100644 --- a/Source/NETworkManager/ViewModels/LookupOUILookupViewModel.cs +++ b/Source/NETworkManager/ViewModels/LookupOUILookupViewModel.cs @@ -71,7 +71,7 @@ private void AddSearchToHistory(string macAddressOrVendor) #endregion #region Variables - + /// /// The logger. /// diff --git a/Source/NETworkManager/ViewModels/LookupPortViewModel.cs b/Source/NETworkManager/ViewModels/LookupPortViewModel.cs index e321e447b0..7051fa87f0 100644 --- a/Source/NETworkManager/ViewModels/LookupPortViewModel.cs +++ b/Source/NETworkManager/ViewModels/LookupPortViewModel.cs @@ -67,7 +67,7 @@ private void AddSearchToHistory(string portOrService) #endregion #region Variables - + /// /// The logger. /// diff --git a/Source/NETworkManager/ViewModels/NetworkConnectionWidgetViewModel.cs b/Source/NETworkManager/ViewModels/NetworkConnectionWidgetViewModel.cs index 50cee9098d..db3617750a 100644 --- a/Source/NETworkManager/ViewModels/NetworkConnectionWidgetViewModel.cs +++ b/Source/NETworkManager/ViewModels/NetworkConnectionWidgetViewModel.cs @@ -19,7 +19,7 @@ namespace NETworkManager.ViewModels; public class NetworkConnectionWidgetViewModel : ViewModelBase { #region Variables - + /// /// The logger. /// @@ -657,7 +657,7 @@ public void Check() { CheckAsync().ConfigureAwait(false); } - + /// /// The cancellation token source. /// @@ -667,7 +667,7 @@ public void Check() /// The check task. /// private Task _checkTask = Task.CompletedTask; - + /// /// Checks the network connections asynchronously. /// @@ -714,7 +714,7 @@ private async Task CheckAsync() Log.Info("Network connection check completed."); } } - + /// /// Runs the check tasks. /// @@ -728,7 +728,7 @@ await Task.WhenAll( CheckConnectionInternetAsync(ct) ); } - + /// /// Checks the computer connection. /// @@ -1071,7 +1071,7 @@ private Task CheckConnectionInternetAsync(CancellationToken ct) var result = await httpResponse.Content.ReadAsStringAsync(ct); - var match = Regex.Match(result, RegexHelper.IPv4AddressExtractRegex); + var match = RegexHelper.IPv4AddressExtractRegex().Match(result); if (match.Success) { diff --git a/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs b/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs index cd04f600ca..c2366c25dc 100644 --- a/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs @@ -338,7 +338,7 @@ public bool IsProfileFilterSet } private readonly GroupExpanderStateStore _groupExpanderStateStore = new(); - + /// /// Gets the group expander state store. /// diff --git a/Source/NETworkManager/ViewModels/PortScannerHostViewModel.cs b/Source/NETworkManager/ViewModels/PortScannerHostViewModel.cs index 07dc2fafdf..3b0e188816 100644 --- a/Source/NETworkManager/ViewModels/PortScannerHostViewModel.cs +++ b/Source/NETworkManager/ViewModels/PortScannerHostViewModel.cs @@ -505,7 +505,7 @@ private void CollapseAllProfileGroupsAction() { SetIsExpandedForAllProfileGroups(false); } - + /// /// Gets the callback for closing a tab item. /// diff --git a/Source/NETworkManager/ViewModels/SNTPLookupSettingsViewModel.cs b/Source/NETworkManager/ViewModels/SNTPLookupSettingsViewModel.cs index 09ac160119..70eae4e110 100644 --- a/Source/NETworkManager/ViewModels/SNTPLookupSettingsViewModel.cs +++ b/Source/NETworkManager/ViewModels/SNTPLookupSettingsViewModel.cs @@ -137,52 +137,59 @@ private void DeleteServerAction() private async Task AddServer() { - var customDialog = new CustomDialog - { - Title = Strings.AddSNTPServer - }; + var childWindow = new ServerConnectionInfoProfileChildWindow(); - var viewModel = new ServerConnectionInfoProfileViewModel(instance => - { - _dialogCoordinator.HideMetroDialogAsync(this, customDialog); + var childWindowViewModel = new ServerConnectionInfoProfileViewModel(instance => + { + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; - SettingsManager.Current.SNTPLookup_SNTPServers.Add( - new ServerConnectionInfoProfile(instance.Name, instance.Servers.ToList())); - }, _ => { _dialogCoordinator.HideMetroDialogAsync(this, customDialog); }, + SettingsManager.Current.SNTPLookup_SNTPServers.Add( + new ServerConnectionInfoProfile(instance.Name, [.. instance.Servers])); + }, _ => + { + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; + }, (ServerInfoProfileNames, false, false), _profileDialogDefaultValues); - customDialog.Content = new ServerConnectionInfoProfileDialog - { - DataContext = viewModel - }; - await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog); + childWindow.Title = Strings.AddSNTPServer; + + childWindow.DataContext = childWindowViewModel; + + ConfigurationManager.Current.IsChildWindowOpen = true; + + await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow); } public async Task EditServer() { - var customDialog = new CustomDialog + var childWindow = new ServerConnectionInfoProfileChildWindow(); + + var childWindowViewModel = new ServerConnectionInfoProfileViewModel(instance => { - Title = Strings.EditSNTPServer - }; - - var viewModel = new ServerConnectionInfoProfileViewModel(instance => - { - _dialogCoordinator.HideMetroDialogAsync(this, customDialog); - - SettingsManager.Current.SNTPLookup_SNTPServers.Remove(SelectedSNTPServer); - SettingsManager.Current.SNTPLookup_SNTPServers.Add( - new ServerConnectionInfoProfile(instance.Name, instance.Servers.ToList())); - }, _ => { _dialogCoordinator.HideMetroDialogAsync(this, customDialog); }, - (ServerInfoProfileNames, true, false), - _profileDialogDefaultValues, SelectedSNTPServer); + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; - customDialog.Content = new ServerConnectionInfoProfileDialog + SettingsManager.Current.SNTPLookup_SNTPServers.Remove(SelectedSNTPServer); + SettingsManager.Current.SNTPLookup_SNTPServers.Add( + new ServerConnectionInfoProfile(instance.Name, [.. instance.Servers])); + }, _ => { - DataContext = viewModel - }; + childWindow.IsOpen = false; + ConfigurationManager.Current.IsChildWindowOpen = false; + }, + (ServerInfoProfileNames, true, false), + _profileDialogDefaultValues, SelectedSNTPServer); + + childWindow.Title = Strings.EditSNTPServer; + + childWindow.DataContext = childWindowViewModel; + + ConfigurationManager.Current.IsChildWindowOpen = true; - await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog); + await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow); } private async Task DeleteServer() diff --git a/Source/NETworkManager/ViewModels/SubnetCalculatorSubnettingViewModel.cs b/Source/NETworkManager/ViewModels/SubnetCalculatorSubnettingViewModel.cs index 6c157077a6..760c91ac70 100644 --- a/Source/NETworkManager/ViewModels/SubnetCalculatorSubnettingViewModel.cs +++ b/Source/NETworkManager/ViewModels/SubnetCalculatorSubnettingViewModel.cs @@ -15,7 +15,6 @@ using System.Linq; using System.Net; using System.Net.Sockets; -using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; @@ -249,7 +248,7 @@ private async Task Calculate() var newCidr = // Support subnetmask like 255.255.255.0 - Regex.IsMatch(newSubnetmaskOrCidr, RegexHelper.SubnetmaskRegex) + RegexHelper.SubnetmaskRegex().IsMatch(newSubnetmaskOrCidr) ? Convert.ToByte(Subnetmask.ConvertSubnetmaskToCidr(IPAddress.Parse(newSubnetmaskOrCidr))) : Convert.ToByte(newSubnetmaskOrCidr.TrimStart('/')); diff --git a/Source/NETworkManager/Views/DNSLookupView.xaml b/Source/NETworkManager/Views/DNSLookupView.xaml index 7917c324aa..904c9ef194 100644 --- a/Source/NETworkManager/Views/DNSLookupView.xaml +++ b/Source/NETworkManager/Views/DNSLookupView.xaml @@ -19,7 +19,6 @@ - @@ -71,15 +70,18 @@ - - - - - - + + + + + + + + @@ -96,7 +98,7 @@ Value="True"> - diff --git a/Source/NETworkManager/Views/ServerConnectionInfoProfileDialog.xaml b/Source/NETworkManager/Views/ServerConnectionInfoProfileChildWindow.xaml similarity index 80% rename from Source/NETworkManager/Views/ServerConnectionInfoProfileDialog.xaml rename to Source/NETworkManager/Views/ServerConnectionInfoProfileChildWindow.xaml index 9333725433..8e2cadf357 100644 --- a/Source/NETworkManager/Views/ServerConnectionInfoProfileDialog.xaml +++ b/Source/NETworkManager/Views/ServerConnectionInfoProfileChildWindow.xaml @@ -1,4 +1,4 @@ - - + xmlns:localization="clr-namespace:NETworkManager.Localization.Resources;assembly=NETworkManager.Localization" + xmlns:interactivity="http://schemas.microsoft.com/xaml/behaviors" + xmlns:converters="clr-namespace:NETworkManager.Converters;assembly=NETworkManager.Converters" + xmlns:simpleChildWindow="clr-namespace:MahApps.Metro.SimpleChildWindow;assembly=MahApps.Metro.SimpleChildWindow" + CloseButtonCommand="{Binding Path=CancelCommand}" + Style="{StaticResource DefaultChildWindow}" + Loaded="ChildWindow_OnLoaded" + mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:ServerConnectionInfoProfileViewModel}"> + @@ -38,7 +43,7 @@ + mah:TextBoxHelper.Watermark="{x:Static localization:StaticStrings.ExampleGroupServers}"> @@ -49,7 +54,7 @@ + UsedNames="{Binding Data.UsedNames, Source={StaticResource BindingProxy}}" /> @@ -57,9 +62,9 @@ - + @@ -75,13 +80,13 @@ + Binding="{Binding (network:ServerConnectionInfo.Server)}" + SortMemberPath="Server" + Width="250" /> + SortMemberPath="Port" + Binding="{Binding (network:ServerConnectionInfo.Port)}" + Width="*" /> @@ -91,7 +96,7 @@ - + @@ -101,7 +106,7 @@ + mah:TextBoxHelper.Watermark="{Binding ServerWatermark}"> @@ -112,7 +117,7 @@ + AllowOnlyIPAddress="{Binding Data.AllowOnlyIPAddress, Source={StaticResource BindingProxy}}" /> @@ -120,7 +125,7 @@ + mah:TextBoxHelper.Watermark="{Binding PortWatermark}"> @@ -130,7 +135,7 @@