From 9b500dbf697645dc640498a65228b4aee85e8e60 Mon Sep 17 00:00:00 2001 From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> Date: Thu, 4 Dec 2025 00:32:32 +0100 Subject: [PATCH 1/5] Feature: DNS Lookup - Input server --- .../DNSServerConnectionInfoProfileToString.cs | 23 ------- .../Network/DNSServer.cs | 36 +++++------ .../Network/DNSServerConnectionInfoProfile.cs | 3 +- .../Network/ServerConnectionInfoProfile.cs | 7 +- .../NETworkManager.Settings/SettingsInfo.cs | 2 +- .../ViewModels/DNSLookupSettingsViewModel.cs | 2 +- .../ViewModels/DNSLookupViewModel.cs | 64 +++++++++++++++++-- .../NETworkManager/Views/DNSLookupView.xaml | 17 ++--- 8 files changed, 92 insertions(+), 62 deletions(-) delete mode 100644 Source/NETworkManager.Converters/DNSServerConnectionInfoProfileToString.cs 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.Models/Network/DNSServer.cs b/Source/NETworkManager.Models/Network/DNSServer.cs index e654d973cb..13cb3d7cd4 100644 --- a/Source/NETworkManager.Models/Network/DNSServer.cs +++ b/Source/NETworkManager.Models/Network/DNSServer.cs @@ -13,34 +13,34 @@ 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("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..32737e9f71 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; } @@ -28,5 +29,5 @@ public DNSServerConnectionInfoProfile(string name, List se /// /// Use the DNS server from Windows. /// - public bool UseWindowsDNSServer { get; set; } + public bool UseWindowsDNSServer { get; set; } } \ No newline at end of file diff --git a/Source/NETworkManager.Models/Network/ServerConnectionInfoProfile.cs b/Source/NETworkManager.Models/Network/ServerConnectionInfoProfile.cs index 8d4848d83c..3c9afed439 100644 --- a/Source/NETworkManager.Models/Network/ServerConnectionInfoProfile.cs +++ b/Source/NETworkManager.Models/Network/ServerConnectionInfoProfile.cs @@ -33,5 +33,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..428cd23715 100644 --- a/Source/NETworkManager.Settings/SettingsInfo.cs +++ b/Source/NETworkManager.Settings/SettingsInfo.cs @@ -1753,7 +1753,7 @@ public ObservableCollection DNSLookup_DNSServers } } - private DNSServerConnectionInfoProfile _dnsLookup_SelectedDNSServer = new(); + private DNSServerConnectionInfoProfile _dnsLookup_SelectedDNSServer = null; public DNSServerConnectionInfoProfile DNSLookup_SelectedDNSServer { diff --git a/Source/NETworkManager/ViewModels/DNSLookupSettingsViewModel.cs b/Source/NETworkManager/ViewModels/DNSLookupSettingsViewModel.cs index 1bcfe63d1d..32e5431643 100644 --- a/Source/NETworkManager/ViewModels/DNSLookupSettingsViewModel.cs +++ b/Source/NETworkManager/ViewModels/DNSLookupSettingsViewModel.cs @@ -28,7 +28,7 @@ public class DNSLookupSettingsViewModel : ViewModelBase public ICollectionView DNSServers { get; } - private DNSServerConnectionInfoProfile _selectedDNSServer = new(); + private DNSServerConnectionInfoProfile _selectedDNSServer = null; public DNSServerConnectionInfoProfile SelectedDNSServer { diff --git a/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs b/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs index 288cef0176..d70fbde655 100644 --- a/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs +++ b/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs @@ -16,6 +16,7 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; +using System.Net; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; @@ -57,24 +58,69 @@ public string Host public ICollectionView DNSServers { get; } private DNSServerConnectionInfoProfile _dnsServer = new(); - public DNSServerConnectionInfoProfile DNSServer { get => _dnsServer; - set + private set { - if (value == _dnsServer) + if (_dnsServer == value) return; + + _dnsServer = value ?? new DNSServerConnectionInfoProfile(); if (!_isLoading) - SettingsManager.Current.DNSLookup_SelectedDNSServer = value; + SettingsManager.Current.DNSLookup_SelectedDNSServer = _dnsServer; + + OnPropertyChanged(); + } + } + + private DNSServerConnectionInfoProfile _selectedListProfile; + public DNSServerConnectionInfoProfile SelectedListProfile + { + get => _selectedListProfile; + set + { + if (_selectedListProfile == value) + return; + + if (value != null) + { + DNSServer = value; + DNSServerQuickInput = value.ToString(); // uses your override + } - _dnsServer = value; + _selectedListProfile = value; OnPropertyChanged(); } } - private List _queryTypes = new(); + // Text box content + private string _dnsServerQuickInput = string.Empty; + public string DNSServerQuickInput + { + get => _dnsServerQuickInput; + set + { + if (_dnsServerQuickInput == value) + return; + + _dnsServerQuickInput = value?.Trim() ?? string.Empty; + OnPropertyChanged(); + + // As soon as user types → deselect any list item + SelectedListProfile = null; + + // Create custom profile from raw IP + if (IPAddress.TryParse(_dnsServerQuickInput, out IPAddress x)) + { + // Temporarily switch to this custom profile + DNSServer = new DNSServerConnectionInfoProfile("CUSTOM", [new ServerConnectionInfo(x.ToString(), 53)]); + } + } + } + + private List _queryTypes = []; public List QueryTypes { @@ -220,10 +266,14 @@ public DNSLookupViewModel(IDialogCoordinator instance, Guid tabId, string host) ListSortDirection.Descending)); DNSServers.SortDescriptions.Add(new SortDescription(nameof(DNSServerConnectionInfoProfile.Name), ListSortDirection.Ascending)); - DNSServer = DNSServers.SourceCollection.Cast() + var initialDNSServer = DNSServers.SourceCollection.Cast() .FirstOrDefault(x => x.Name == SettingsManager.Current.DNSLookup_SelectedDNSServer.Name) ?? DNSServers.SourceCollection.Cast().First(); + DNSServer = initialDNSServer; + SelectedListProfile = initialDNSServer; + DNSServerQuickInput = initialDNSServer.ToString(); + ResultsView = CollectionViewSource.GetDefaultView(Results); ResultsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(DNSLookupRecordInfo.NameServerAsString))); ResultsView.SortDescriptions.Add(new SortDescription(nameof(DNSLookupRecordInfo.NameServerIPAddress), diff --git a/Source/NETworkManager/Views/DNSLookupView.xaml b/Source/NETworkManager/Views/DNSLookupView.xaml index 7917c324aa..faab72a4eb 100644 --- a/Source/NETworkManager/Views/DNSLookupView.xaml +++ b/Source/NETworkManager/Views/DNSLookupView.xaml @@ -19,7 +19,6 @@ - @@ -71,15 +70,13 @@ - - - - - - + From 027b11b6cfdfa8b0b08fd63550155a983e39687d Mon Sep 17 00:00:00 2001 From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> Date: Wed, 10 Dec 2025 22:49:26 +0100 Subject: [PATCH 2/5] Feature: Enter dns server in ui --- Source/GlobalAssemblyInfo.cs | 4 +- .../Network/DNSLookupErrorArgs.cs | 1 + .../Network/DNSServerConnectionInfoProfile.cs | 1 + .../NETworkManager.Settings/SettingsInfo.cs | 28 ++-- .../SettingsManager.cs | 10 ++ .../ViewModels/DNSLookupViewModel.cs | 142 +++++++++++------- .../NETworkManager/Views/DNSLookupView.xaml | 8 +- 7 files changed, 117 insertions(+), 77 deletions(-) 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.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/DNSServerConnectionInfoProfile.cs b/Source/NETworkManager.Models/Network/DNSServerConnectionInfoProfile.cs index 32737e9f71..c1ba8003f2 100644 --- a/Source/NETworkManager.Models/Network/DNSServerConnectionInfoProfile.cs +++ b/Source/NETworkManager.Models/Network/DNSServerConnectionInfoProfile.cs @@ -24,6 +24,7 @@ public DNSServerConnectionInfoProfile() /// List of servers as . public DNSServerConnectionInfoProfile(string name, List servers) : base(name, servers) { + } /// diff --git a/Source/NETworkManager.Settings/SettingsInfo.cs b/Source/NETworkManager.Settings/SettingsInfo.cs index 428cd23715..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 } } + [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/ViewModels/DNSLookupViewModel.cs b/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs index fb45193397..ad963abb08 100644 --- a/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs +++ b/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs @@ -81,77 +81,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; - private set + set { if (_dnsServer == value) return; - - _dnsServer = value ?? new DNSServerConnectionInfoProfile(); - if (!_isLoading) - SettingsManager.Current.DNSLookup_SelectedDNSServer = _dnsServer; - - OnPropertyChanged(); - } - } + // Try finding matching dns server profile by name, otherwise set to null (de-select) + SelectedDNSServer = SettingsManager.Current.DNSLookup_DNSServers + .FirstOrDefault(x => x.Name == value); - private DNSServerConnectionInfoProfile _selectedListProfile; - public DNSServerConnectionInfoProfile SelectedListProfile - { - get => _selectedListProfile; - set - { - if (_selectedListProfile == value) - return; - - if (value != null) - { - DNSServer = value; - DNSServerQuickInput = value.ToString(); // uses your override - } + if (!_isLoading) + SettingsManager.Current.DNSLookup_SelectedDNSServer_v2 = value; - _selectedListProfile = value; + _dnsServer = value; OnPropertyChanged(); } } - // Text box content - private string _dnsServerQuickInput = string.Empty; - public string DNSServerQuickInput + private DNSServerConnectionInfoProfile _selectedDNSServer; + public DNSServerConnectionInfoProfile SelectedDNSServer { - get => _dnsServerQuickInput; + get => _selectedDNSServer; set { - if (_dnsServerQuickInput == value) + if (_selectedDNSServer == value) return; - _dnsServerQuickInput = value?.Trim() ?? string.Empty; + _selectedDNSServer = value; OnPropertyChanged(); - - // As soon as user types → deselect any list item - SelectedListProfile = null; - - // Create custom profile from raw IP - if (IPAddress.TryParse(_dnsServerQuickInput, out IPAddress x)) - { - // Temporarily switch to this custom profile - DNSServer = new DNSServerConnectionInfoProfile("CUSTOM", [new ServerConnectionInfo(x.ToString(), 53)]); - } } } /// /// Backing field for . /// - private List _queryTypes = new(); + private List _queryTypes = []; /// /// Gets the list of available query types. @@ -217,7 +190,7 @@ public bool IsRunning /// /// Backing field for . /// - private ObservableCollection _results = new(); + private ObservableCollection _results = []; /// /// Gets or sets the collection of lookup results. @@ -351,13 +324,10 @@ public DNSLookupViewModel(IDialogCoordinator instance, Guid tabId, string host) ListSortDirection.Descending)); DNSServers.SortDescriptions.Add(new SortDescription(nameof(DNSServerConnectionInfoProfile.Name), ListSortDirection.Ascending)); - var initialDNSServer = DNSServers.SourceCollection.Cast() - .FirstOrDefault(x => x.Name == SettingsManager.Current.DNSLookup_SelectedDNSServer.Name) ?? - DNSServers.SourceCollection.Cast().First(); - - DNSServer = initialDNSServer; - SelectedListProfile = initialDNSServer; - DNSServerQuickInput = initialDNSServer.ToString(); + + 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))); @@ -381,7 +351,7 @@ public void OnLoaded() return; if (!string.IsNullOrEmpty(Host)) - Query(); + QueryAsync(); _firstLoad = false; } @@ -441,7 +411,7 @@ private bool Query_CanExecute(object parameter) private void QueryAction() { if (!IsRunning) - Query(); + QueryAsync(); } /// @@ -464,7 +434,7 @@ private void ExportAction() /// /// Performs the DNS query. /// - private void Query() + private async Task QueryAsync() { IsStatusMessageDisplayed = false; StatusMessage = string.Empty; @@ -496,9 +466,67 @@ private void Query() dnsSettings.CustomDNSSuffix = SettingsManager.Current.DNSLookup_CustomDNSSuffix?.TrimStart('.'); } - var dnsLookup = DNSServer.UseWindowsDNSServer - ? new DNSLookup(dnsSettings) - : new DNSLookup(dnsSettings, DNSServer.Servers); + + DNSLookup dnsLookup; + + // Try find existing dns server profile + var dnsServerProfile = SettingsManager.Current.DNSLookup_DNSServers + .FirstOrDefault(x => x.Name == DNSServer); + + // Use profile if found + if (dnsServerProfile != null) + { + dnsLookup = dnsServerProfile.UseWindowsDNSServer + ? new DNSLookup(dnsSettings) + : new DNSLookup(dnsSettings, dnsServerProfile.Servers); + } + // Otherwise try to parse custom server string + else + { + List customDNSServers = []; + + foreach (var customDNSServer in DNSServer.Split(';').Select(x => x.Trim())) + { + var customDNSServerArgs = customDNSServer.Split(':'); + + var server = customDNSServerArgs[0]; + var port = customDNSServerArgs.Length == 2 && int.TryParse(customDNSServerArgs[1], out var p) ? p : 53; + + // Resolve hostname to IP address + if (!IPAddress.TryParse(server, out _)) + { + var dnsResult = await DNSClientHelper.ResolveAorAaaaAsync(server, + SettingsManager.Current.Network_ResolveHostnamePreferIPv4); + + if (dnsResult.HasError) + { + var dnsErrorMessage = DNSClientHelper.FormatDNSClientResultError(server, dnsResult); + + if(!string.IsNullOrEmpty(StatusMessage)) + StatusMessage += Environment.NewLine; + + StatusMessage += $"{Strings.DNSServer}: {dnsErrorMessage}"; + IsStatusMessageDisplayed = true; + + continue; // Skip this server, try next one + } + + server = dnsResult.Value.ToString(); + } + + customDNSServers.Add(new ServerConnectionInfo(server, port, TransportProtocol.Udp)); + } + + // Check if we have any valid custom dns servers + if (customDNSServers.Count == 0) + { + IsRunning = false; + + return; + } + + dnsLookup = new DNSLookup(dnsSettings, customDNSServers); + } dnsLookup.RecordReceived += DNSLookup_RecordReceived; dnsLookup.LookupError += DNSLookup_LookupError; diff --git a/Source/NETworkManager/Views/DNSLookupView.xaml b/Source/NETworkManager/Views/DNSLookupView.xaml index faab72a4eb..fc2efbd7f6 100644 --- a/Source/NETworkManager/Views/DNSLookupView.xaml +++ b/Source/NETworkManager/Views/DNSLookupView.xaml @@ -71,10 +71,10 @@ From 37d77447a450dc35f290f5305d4edf946a29fe0c Mon Sep 17 00:00:00 2001 From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> Date: Wed, 10 Dec 2025 23:07:24 +0100 Subject: [PATCH 3/5] Update DNSLookupView.xaml --- Source/NETworkManager/Views/DNSLookupView.xaml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Source/NETworkManager/Views/DNSLookupView.xaml b/Source/NETworkManager/Views/DNSLookupView.xaml index fc2efbd7f6..904c9ef194 100644 --- a/Source/NETworkManager/Views/DNSLookupView.xaml +++ b/Source/NETworkManager/Views/DNSLookupView.xaml @@ -73,10 +73,15 @@ + SelectedItem="{Binding SelectedDNSServer}" + IsEditable="True"> + + + + + + + @@ -93,7 +98,7 @@ Value="True"> - From 20bd604d06b86b07751befb18d6ab8eaaaf1085f Mon Sep 17 00:00:00 2001 From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> Date: Wed, 10 Dec 2025 23:35:05 +0100 Subject: [PATCH 4/5] Chore: Adjust regex for IPv4 --- ...lOrEmptyOrIPv4AddressToBooleanConverter.cs | 7 ++-- .../Network/HostRangeHelper.cs | 9 +++-- .../Network/SNTPLookup.cs | 7 ++-- .../NETworkManager.Utilities/RegexHelper.cs | 40 ++++++++++++++----- .../EmptyOrIPv4AddressValidator.cs | 9 +++-- .../IPAddressOrHostnameAsRangeValidator.cs | 2 +- .../IPAddressOrHostnameValidator.cs | 2 +- .../IPv4AddressValidator.cs | 9 ++--- .../MultipleHostsRangeValidator.cs | 12 +++--- .../MultipleIPAddressesValidator.cs | 12 +++--- .../ServerValidator.cs | 4 +- .../NetworkConnectionWidgetViewModel.cs | 2 +- .../ServerConnectionInfoProfileDialog.xaml | 2 +- 13 files changed, 68 insertions(+), 49 deletions(-) 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.Models/Network/HostRangeHelper.cs b/Source/NETworkManager.Models/Network/HostRangeHelper.cs index 9017e5ce80..37928f8368 100644 --- a/Source/NETworkManager.Models/Network/HostRangeHelper.cs +++ b/Source/NETworkManager.Models/Network/HostRangeHelper.cs @@ -1,4 +1,6 @@ -using System.Collections.Concurrent; +using ControlzEx.Standard; +using NETworkManager.Utilities; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net; @@ -6,7 +8,6 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using NETworkManager.Utilities; namespace NETworkManager.Models.Network; @@ -46,7 +47,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)); @@ -71,7 +72,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])), 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.Utilities/RegexHelper.cs b/Source/NETworkManager.Utilities/RegexHelper.cs index c1445c70a7..ba09fd7ce2 100644 --- a/Source/NETworkManager.Utilities/RegexHelper.cs +++ b/Source/NETworkManager.Utilities/RegexHelper.cs @@ -1,6 +1,8 @@ -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 @@ -10,20 +12,36 @@ public static class RegexHelper @"((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 + /// Provides a compiled regular expression that matches valid IPv4 addresses in dot-decimal notation. /// - // ReSharper disable once InconsistentNaming - public const string IPv4AddressRegex = $"^{IPv4AddressValues}$"; + /// The returned regular expression is compiled for performance. Use this regex to validate or + /// extract IPv4 addresses from text. The pattern enforces correct octet ranges and dot separators. + /// 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(); /// - /// Match IPv4-Address within a string + /// Provides a compiled regular expression that matches valid IPv4 addresses within input text. /// - // ReSharper disable once InconsistentNaming - public const string IPv4AddressExtractRegex = IPv4AddressValues; + /// The returned regular expression matches IPv4 addresses in standard dotted-decimal notation + /// (e.g., "192.168.1.1"). The regular expression is compiled for improved performance when used + /// repeatedly. + /// A instance that can be used to extract IPv4 addresses from strings. + [GeneratedRegex(IPv4AddressValues)] + public static partial Regex IPv4AddressExtractRegex(); - // Match IPv4-Address Range like 192.168.178.1-192.168.178.127 - // ReSharper disable once InconsistentNaming - public const string IPv4AddressRangeRegex = $"^{IPv4AddressValues}-{IPv4AddressValues}$"; + /// + /// Gets a regular expression that matches an IPv4 address range in the format "start-end", where both start and end + /// are valid IPv4 addresses. + /// + /// The regular expression expects the input to consist of two IPv4 addresses separated by a + /// hyphen, with no additional whitespace or characters. Both addresses must be valid IPv4 addresses. This can be + /// used to validate or parse address range strings in network configuration scenarios. + /// 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(); // 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 = 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/IPAddressOrHostnameAsRangeValidator.cs b/Source/NETworkManager.Validators/IPAddressOrHostnameAsRangeValidator.cs index 8392c450dc..ffb76cbb75 100644 --- a/Source/NETworkManager.Validators/IPAddressOrHostnameAsRangeValidator.cs +++ b/Source/NETworkManager.Validators/IPAddressOrHostnameAsRangeValidator.cs @@ -20,7 +20,7 @@ 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); diff --git a/Source/NETworkManager.Validators/IPAddressOrHostnameValidator.cs b/Source/NETworkManager.Validators/IPAddressOrHostnameValidator.cs index 3aa1980789..3126b87a16 100644 --- a/Source/NETworkManager.Validators/IPAddressOrHostnameValidator.cs +++ b/Source/NETworkManager.Validators/IPAddressOrHostnameValidator.cs @@ -16,7 +16,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 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/MultipleHostsRangeValidator.cs b/Source/NETworkManager.Validators/MultipleHostsRangeValidator.cs index aa6f4bba96..5b3bdf9bc3 100644 --- a/Source/NETworkManager.Validators/MultipleHostsRangeValidator.cs +++ b/Source/NETworkManager.Validators/MultipleHostsRangeValidator.cs @@ -1,10 +1,10 @@ -using System.Globalization; +using NETworkManager.Localization.Resources; +using NETworkManager.Models.Network; +using NETworkManager.Utilities; +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,7 +20,7 @@ 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 @@ -32,7 +32,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) continue; // 192.168.0.0 - 192.168.0.100 - if (Regex.IsMatch(ipHostOrRange, RegexHelper.IPv4AddressRangeRegex)) + if (RegexHelper.IPv4AddressRangeRegex().IsMatch(ipHostOrRange)) { var range = ipHostOrRange.Split('-'); 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/ServerValidator.cs b/Source/NETworkManager.Validators/ServerValidator.cs index bd82d4f88c..ca2d381cd3 100644 --- a/Source/NETworkManager.Validators/ServerValidator.cs +++ b/Source/NETworkManager.Validators/ServerValidator.cs @@ -6,7 +6,7 @@ namespace NETworkManager.Validators; -public class ServerValidator : ValidationRule +public partial class ServerValidator : ValidationRule { public ServerDependencyObjectWrapper Wrapper { get; set; } @@ -22,7 +22,7 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo) 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 diff --git a/Source/NETworkManager/ViewModels/NetworkConnectionWidgetViewModel.cs b/Source/NETworkManager/ViewModels/NetworkConnectionWidgetViewModel.cs index 50cee9098d..5a0dc1e3bc 100644 --- a/Source/NETworkManager/ViewModels/NetworkConnectionWidgetViewModel.cs +++ b/Source/NETworkManager/ViewModels/NetworkConnectionWidgetViewModel.cs @@ -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/Views/ServerConnectionInfoProfileDialog.xaml b/Source/NETworkManager/Views/ServerConnectionInfoProfileDialog.xaml index 9333725433..db26ad245e 100644 --- a/Source/NETworkManager/Views/ServerConnectionInfoProfileDialog.xaml +++ b/Source/NETworkManager/Views/ServerConnectionInfoProfileDialog.xaml @@ -198,4 +198,4 @@ Style="{StaticResource DefaultButton}" /> - \ No newline at end of file + From eddba620ea6c7a9a11b5ddeba7820da6c7ad1b93 Mon Sep 17 00:00:00 2001 From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> Date: Wed, 10 Dec 2025 23:55:37 +0100 Subject: [PATCH 5/5] Chore: Migrate regex to generatedRegex --- ...dateSubnetCalculatorSubnettingConverter.cs | 3 +- .../Network/HostRangeHelper.cs | 6 +- .../NETworkManager.Utilities/RegexHelper.cs | 87 +++++++++++++------ .../IPv4IPv6SubnetValidator.cs | 4 +- .../IPv4IPv6SubnetmaskOrCIDRValidator.cs | 3 +- .../IPv4SubnetValidator.cs | 5 +- .../IPv4SubnetmaskOrCIDRValidator.cs | 3 +- .../MultipleHostsRangeValidator.cs | 6 +- .../SubnetmaskValidator.cs | 3 +- .../SubnetCalculatorSubnettingViewModel.cs | 3 +- Website/docs/changelog/next-release.md | 1 + 11 files changed, 76 insertions(+), 48 deletions(-) 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.Models/Network/HostRangeHelper.cs b/Source/NETworkManager.Models/Network/HostRangeHelper.cs index 37928f8368..e7e05922c6 100644 --- a/Source/NETworkManager.Models/Network/HostRangeHelper.cs +++ b/Source/NETworkManager.Models/Network/HostRangeHelper.cs @@ -55,9 +55,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, @@ -87,7 +87,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>(); diff --git a/Source/NETworkManager.Utilities/RegexHelper.cs b/Source/NETworkManager.Utilities/RegexHelper.cs index ba09fd7ce2..d9d48ee014 100644 --- a/Source/NETworkManager.Utilities/RegexHelper.cs +++ b/Source/NETworkManager.Utilities/RegexHelper.cs @@ -5,12 +5,22 @@ 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]?)"; + /// + /// 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])"; + /// /// Provides a compiled regular expression that matches valid IPv4 addresses in dot-decimal notation. /// @@ -43,6 +53,51 @@ public static partial class RegexHelper [GeneratedRegex($"^{IPv4AddressValues}-{IPv4AddressValues}$")] public static partial Regex IPv4AddressRangeRegex(); + /// + /// Provides a compiled regular expression that matches valid IPv4 subnet mask values. + /// + /// The returned regular expression is generated at compile time and is optimized for + /// performance. Use this regex to validate or parse subnet mask strings in IPv4 networking scenarios. + /// A instance that matches strings representing valid IPv4 subnet masks. + [GeneratedRegex($"^{SubnetmaskValues}$")] + public static partial Regex SubnetmaskRegex(); + + /// + /// Provides a compiled regular expression that matches IPv4 addresses with subnet masks in CIDR notation, such as + /// "192.168.178.0/255.255.255.0". + /// + /// The returned regular expression validates both the IPv4 address and the subnet mask + /// components. Use this regex to verify input strings representing IPv4 subnets in formats like + /// "address/mask". + /// 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 in CIDR notation, such as + /// "192.168.178.0/24". + /// + /// The returned regular expression can be used to validate or extract IPv4 addresses with CIDR + /// notation, such as "192.168.1.0/24". The pattern enforces correct formatting for both the address and the prefix + /// length. + /// 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(); + + /// + /// Creates a regular expression that matches IPv4 addresses, allowing for a special range in one or more octets. + /// + /// The returned regular expression matches standard IPv4 addresses and addresses where one or + /// more octets are defined by a custom range pattern. This is useful for validating or parsing addresses such as + /// "192.168.[50-100].1" where a range is specified in place of an octet. The format and behavior of the special + /// range are determined by the SpecialRangeRegex value. + /// A instance that matches IPv4 addresses with support for custom special ranges as defined by + /// SpecialRangeRegex. + [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 = @"^^[A-Fa-f0-9]{12}$|^[A-Fa-f0-9]{2}(:|-){1}[A-Fa-f0-9]{2}(:|-){1}[A-Fa-f0-9]{2}(:|-){1}[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]{4}.[A-Fa-f0-9]{4}$$"; @@ -50,24 +105,7 @@ public static partial class RegexHelper // Match the first 3 bytes of a MAC-Address 000000, 00:00:00, 00-00-00 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 = @@ -82,11 +120,6 @@ public static partial 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}(?