From 8e1158f0404da05a087c7a782374d2124f0c7575 Mon Sep 17 00:00:00 2001 From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> Date: Wed, 5 Aug 2026 21:26:45 +0200 Subject: [PATCH 1/3] Feature: Remove ThreadPool workaround and make scanner async --- .../Resources/Strings.Designer.cs | 34 ---- .../Resources/Strings.resx | 15 -- .../Network/HostRangeHelper.cs | 88 +++++----- .../Network/IPScanner.cs | 161 +++++++----------- .../Network/NetBIOSResolver.cs | 11 +- .../Network/PortProbe.cs | 53 ++++++ .../Network/PortScanner.cs | 57 ++----- .../GlobalStaticConfiguration.cs | 7 +- .../NETworkManager.Settings/SettingsInfo.cs | 14 -- Source/NETworkManager/App.xaml.cs | 21 --- .../ViewModels/SettingsGeneralViewModel.cs | 17 -- .../Views/IPScannerSettingsView.xaml | 11 -- .../Views/PortScannerSettingsView.xaml | 11 -- .../Views/SettingsGeneralView.xaml | 15 +- Website/docs/application/ip-scanner.md | 16 +- Website/docs/application/port-scanner.md | 14 +- Website/docs/settings/general.md | 18 +- 17 files changed, 188 insertions(+), 375 deletions(-) create mode 100644 Source/NETworkManager.Models/Network/PortProbe.cs diff --git a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs index d0d46edfd2..b1362fdc88 100644 --- a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs +++ b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs @@ -5283,21 +5283,6 @@ public static string HelpMessage_Tags { } } - /// - /// Looks up a localized string similar to This setting specifies the minimum number of threads that will be created from the application's ThreadPool on demand. This can improve the performance for example of the IP scanner or port scanner. - /// - ///The value is added to the default min. threads (number of CPU threads). The value 0 leaves the default settings. If the value is higher than the default max. threads of the ThreadPool, this value is used. - /// - ///If the value is too high, performance problems may occur. - /// - ///Changes to this value will take effect a [rest of string was truncated]";. - /// - public static string HelpMessage_ThreadPoolAdditionalMinThreads { - get { - return ResourceManager.GetString("HelpMessage_ThreadPoolAdditionalMinThreads", resourceCulture); - } - } - /// /// Looks up a localized string similar to Use custom themes to personalize the appearance of the application. You can edit or add theme in the "Program Folder > Themes" directory. For more details, refer to the documentation.. /// @@ -6891,16 +6876,6 @@ public static string MaxPortThreads { return ResourceManager.GetString("MaxPortThreads", resourceCulture); } } - - /// - /// Looks up a localized string similar to These settings only change the maximum number of concurrently executed threads per host/port scan. Go to Settings > General > General to adjust the (min) threads of the application.. - /// - public static string MaxThreadsOnlyGoToSettingsGeneralGeneral { - get { - return ResourceManager.GetString("MaxThreadsOnlyGoToSettingsGeneralGeneral", resourceCulture); - } - } - /// /// Looks up a localized string similar to Measured time. /// @@ -11944,15 +11919,6 @@ public static string ThisWillResetAllSettings { } } - /// - /// Looks up a localized string similar to ThreadPool additional min. threads. - /// - public static string ThreadPoolAdditionalMinThreads { - get { - return ResourceManager.GetString("ThreadPoolAdditionalMinThreads", resourceCulture); - } - } - /// /// Looks up a localized string similar to Threads. /// diff --git a/Source/NETworkManager.Localization/Resources/Strings.resx b/Source/NETworkManager.Localization/Resources/Strings.resx index 1157005191..0c2584cd75 100644 --- a/Source/NETworkManager.Localization/Resources/Strings.resx +++ b/Source/NETworkManager.Localization/Resources/Strings.resx @@ -3353,21 +3353,6 @@ If the option is disabled again, the values are no longer modified. However, the Ping status - - ThreadPool additional min. threads - - - This setting specifies the minimum number of threads that will be created from the application's ThreadPool on demand. This can improve the performance for example of the IP scanner or port scanner. - -The value is added to the default min. threads (number of CPU threads). The value 0 leaves the default settings. If the value is higher than the default max. threads of the ThreadPool, this value is used. - -If the value is too high, performance problems may occur. - -Changes to this value will take effect after the application is restarted. Whether the value was set successfully can be seen in the log file under %LocalAppData%\NETworkManager\NETworkManager.log - - - These settings only change the maximum number of concurrently executed threads per host/port scan. Go to Settings > General > General to adjust the (min) threads of the application. - Port status diff --git a/Source/NETworkManager.Models/Network/HostRangeHelper.cs b/Source/NETworkManager.Models/Network/HostRangeHelper.cs index 43ccdc505b..fa39524623 100644 --- a/Source/NETworkManager.Models/Network/HostRangeHelper.cs +++ b/Source/NETworkManager.Models/Network/HostRangeHelper.cs @@ -29,19 +29,14 @@ public static IEnumerable CreateListFromInput(string hosts) .ToArray(); } - public static Task<(List<(IPAddress ipAddress, string hostname)> hosts, List hostnamesNotResolved)> + public static async Task<(List<(IPAddress ipAddress, string hostname)> hosts, List hostnamesNotResolved)> ResolveAsync(IEnumerable hosts, bool dnsResolveHostnamePreferIPv4, CancellationToken cancellationToken) - { - return Task.Run(() => Resolve(hosts, dnsResolveHostnamePreferIPv4, cancellationToken), cancellationToken); - } - - private static (List<(IPAddress ipAddress, string hostname)> hosts, List hostnamesNotResolved) Resolve( - IEnumerable hosts, bool dnsResolveHostnamePreferIPv4, CancellationToken cancellationToken) { var hostsBag = new ConcurrentBag<(IPAddress ipAddress, string hostname)>(); var hostnamesNotResovledBag = new ConcurrentBag(); - Parallel.ForEach(hosts, new ParallelOptions { CancellationToken = cancellationToken }, host => + await Parallel.ForEachAsync(hosts, new ParallelOptions { CancellationToken = cancellationToken }, + async (host, ct) => { switch (host) { @@ -62,7 +57,7 @@ private static (List<(IPAddress ipAddress, string hostname)> hosts, List Parallel.For(IPv4Address.ToInt32(network.Network), IPv4Address.ToInt32(network.Broadcast) + 1, (i, state) => { - if (cancellationToken.IsCancellationRequested) + if (ct.IsCancellationRequested) state.Break(); hostsBag.Add((IPv4Address.FromInt32(i), string.Empty)); @@ -77,7 +72,7 @@ private static (List<(IPAddress ipAddress, string hostname)> hosts, List Parallel.For(IPv4Address.ToInt32(IPAddress.Parse(range[0])), IPv4Address.ToInt32(IPAddress.Parse(range[1])) + 1, (i, state) => { - if (cancellationToken.IsCancellationRequested) + if (ct.IsCancellationRequested) state.Break(); hostsBag.Add((IPv4Address.FromInt32(i), string.Empty)); @@ -107,7 +102,7 @@ private static (List<(IPAddress ipAddress, string hostname)> hosts, List Parallel.For(int.Parse(rangeNumbers[0]), int.Parse(rangeNumbers[1]) + 1, (i, state) => { - if (cancellationToken.IsCancellationRequested) + if (ct.IsCancellationRequested) state.Break(); innerList.Add(i); @@ -124,18 +119,18 @@ private static (List<(IPAddress ipAddress, string hostname)> hosts, List } // Build the new ipv4 - Parallel.ForEach(list[0], new ParallelOptions { CancellationToken = cancellationToken }, + Parallel.ForEach(list[0], new ParallelOptions { CancellationToken = ct }, i => { - Parallel.ForEach(list[1], new ParallelOptions { CancellationToken = cancellationToken }, + Parallel.ForEach(list[1], new ParallelOptions { CancellationToken = ct }, j => { Parallel.ForEach(list[2], - new ParallelOptions { CancellationToken = cancellationToken }, + new ParallelOptions { CancellationToken = ct }, k => { Parallel.ForEach(list[3], - new ParallelOptions { CancellationToken = cancellationToken }, + new ParallelOptions { CancellationToken = ct }, h => { hostsBag.Add((IPAddress.Parse($"{i}.{j}.{k}.{h}"), string.Empty)); @@ -148,17 +143,13 @@ private static (List<(IPAddress ipAddress, string hostname)> hosts, List // example.com case var _ when RegexHelper.HostnameOrDomainRegex().IsMatch(host): - using (var dnsResolverTask = - DNSClientHelper.ResolveAorAaaaAsync(host, dnsResolveHostnamePreferIPv4)) - { - // Wait for task inside a Parallel.Foreach - dnsResolverTask.Wait(cancellationToken); + var dnsResult = await DNSClientHelper.ResolveAorAaaaAsync(host, dnsResolveHostnamePreferIPv4) + .ConfigureAwait(false); - if (!dnsResolverTask.Result.HasError) - hostsBag.Add((IPAddress.Parse($"{dnsResolverTask.Result.Value}"), host)); - else - hostnamesNotResovledBag.Add(host); - } + if (!dnsResult.HasError) + hostsBag.Add((IPAddress.Parse($"{dnsResult.Value}"), host)); + else + hostnamesNotResovledBag.Add(host); break; @@ -168,42 +159,39 @@ private static (List<(IPAddress ipAddress, string hostname)> hosts, List var hostAndSubnet = host.Split('/'); // Only support IPv4 - using (var dnsResolverTask = DNSClientHelper.ResolveAorAaaaAsync(hostAndSubnet[0], true)) - { - // Wait for task inside a Parallel.Foreach - dnsResolverTask.Wait(cancellationToken); + var dnsResultWithSubnet = await DNSClientHelper.ResolveAorAaaaAsync(hostAndSubnet[0], true) + .ConfigureAwait(false); - if (!dnsResolverTask.Result.HasError) + if (!dnsResultWithSubnet.HasError) + { + // Only support IPv4 for ranges for now + if (dnsResultWithSubnet.Value.AddressFamily == AddressFamily.InterNetwork) { - // Only support IPv4 for ranges for now - if (dnsResolverTask.Result.Value.AddressFamily == AddressFamily.InterNetwork) - { - network = IPNetwork2.Parse( - $"{dnsResolverTask.Result.Value}/{hostAndSubnet[1]}"); - - Parallel.For(IPv4Address.ToInt32(network.Network), - IPv4Address.ToInt32(network.Broadcast) + 1, (i, state) => - { - if (cancellationToken.IsCancellationRequested) - state.Break(); - - hostsBag.Add((IPv4Address.FromInt32(i), string.Empty)); - }); - } - else - { - hostnamesNotResovledBag.Add(hostAndSubnet[0]); - } + network = IPNetwork2.Parse( + $"{dnsResultWithSubnet.Value}/{hostAndSubnet[1]}"); + + Parallel.For(IPv4Address.ToInt32(network.Network), + IPv4Address.ToInt32(network.Broadcast) + 1, (i, state) => + { + if (ct.IsCancellationRequested) + state.Break(); + + hostsBag.Add((IPv4Address.FromInt32(i), string.Empty)); + }); } else { hostnamesNotResovledBag.Add(hostAndSubnet[0]); } } + else + { + hostnamesNotResovledBag.Add(hostAndSubnet[0]); + } break; } - }); + }).ConfigureAwait(false); // Sort list and return IPAddressComparer comparer = new(); diff --git a/Source/NETworkManager.Models/Network/IPScanner.cs b/Source/NETworkManager.Models/Network/IPScanner.cs index 7fb4e8b698..22f1b4af3f 100644 --- a/Source/NETworkManager.Models/Network/IPScanner.cs +++ b/Source/NETworkManager.Models/Network/IPScanner.cs @@ -79,7 +79,7 @@ public void ScanAsync(IEnumerable<(IPAddress ipAddress, string hostname)> hosts, CancellationToken cancellationToken) { // Start the scan in a separate task - Task.Run(() => + Task.Run(async () => { _progressValue = 0; @@ -101,40 +101,32 @@ public void ScanAsync(IEnumerable<(IPAddress ipAddress, string hostname)> hosts, }; // Start scan - Parallel.ForEach(hosts, hostParallelOptions, host => + await Parallel.ForEachAsync(hosts, hostParallelOptions, async (host, ct) => { - // Start ping async - var pingTask = PingAsync(host.ipAddress, cancellationToken); + // Start ping, port scan and netbios lookup concurrently - none of these block a thread anymore + var pingTask = PingAsync(host.ipAddress, ct); - // Start port scan async (if enabled) var portScanTask = options.PortScanEnabled - ? PortScanAsync(host.ipAddress, portScanParallelOptions, cancellationToken) - : Task.FromResult(Enumerable.Empty()); + ? PortScanAsync(host.ipAddress, portScanParallelOptions, ct) + : Task.FromResult(new List()); - // Start netbios lookup async (if enabled) var netbiosTask = options.NetBIOSEnabled - ? NetBIOSResolver.ResolveAsync(host.ipAddress, options.NetBIOSTimeout, cancellationToken) + ? NetBIOSResolver.ResolveAsync(host.ipAddress, options.NetBIOSTimeout, ct) : Task.FromResult(new NetBIOSInfo(host.ipAddress)); - // Get ping result - pingTask.Wait(cancellationToken); - var pingInfo = pingTask.Result; - - // Get port scan result - portScanTask.Wait(cancellationToken); - var portScanResults = portScanTask.Result.ToList(); + await Task.WhenAll(pingTask, portScanTask, netbiosTask).ConfigureAwait(false); - // Get netbios result - netbiosTask.Wait(cancellationToken); + var pingInfo = pingTask.Result; + var portScanResults = portScanTask.Result; var netBIOSInfo = netbiosTask.Result; // Cancel if the user has canceled - cancellationToken.ThrowIfCancellationRequested(); + ct.ThrowIfCancellationRequested(); // Check if host is up var isAnyPortOpen = portScanResults.Any(x => x.State == PortState.Open); var isReachable = pingInfo.Status == IPStatus.Success || // ICMP response - isAnyPortOpen || // Any port is open + isAnyPortOpen || // Any port is open netBIOSInfo.IsReachable; // NetBIOS response // DNS & ARP @@ -145,14 +137,11 @@ public void ScanAsync(IEnumerable<(IPAddress ipAddress, string hostname)> hosts, if (options.ResolveHostname) { - // Don't use await in Parallel.ForEach, this will break - var dnsResolverTask = DNSClient.GetInstance().ResolvePtrAsync(host.ipAddress); - - // Wait for task inside a Parallel.Foreach - dnsResolverTask.Wait(cancellationToken); + var dnsResult = await DNSClient.GetInstance().ResolvePtrAsync(host.ipAddress) + .ConfigureAwait(false); - if (!dnsResolverTask.Result.HasError) - dnsHostname = dnsResolverTask.Result.Value; + if (!dnsResult.HasError) + dnsHostname = dnsResult.Value; } // ARP @@ -212,7 +201,7 @@ public void ScanAsync(IEnumerable<(IPAddress ipAddress, string hostname)> hosts, } IncreaseProgress(); - }); + }).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -225,101 +214,79 @@ public void ScanAsync(IEnumerable<(IPAddress ipAddress, string hostname)> hosts, }, cancellationToken); } - private Task PingAsync(IPAddress ipAddress, CancellationToken cancellationToken) + private async Task PingAsync(IPAddress ipAddress, CancellationToken cancellationToken) { - return Task.Run(() => + using var ping = new System.Net.NetworkInformation.Ping(); + + for (var i = 0; i < options.ICMPAttempts; i++) { - using var ping = new System.Net.NetworkInformation.Ping(); + // Get timestamp + var timestamp = DateTime.Now; - for (var i = 0; i < options.ICMPAttempts; i++) + try { - // Get timestamp - var timestamp = DateTime.Now; + // Note: the CancellationToken-accepting overload requires a TimeSpan timeout, + // unlike the legacy int-based overloads used elsewhere in .NET's Ping API. + var pingReply = await ping.SendPingAsync(ipAddress, TimeSpan.FromMilliseconds(options.ICMPTimeout), + options.ICMPBuffer, cancellationToken: cancellationToken).ConfigureAwait(false); - try + // Success + if (pingReply is { Status: IPStatus.Success }) { - var pingReply = ping.Send(ipAddress, options.ICMPTimeout, options.ICMPBuffer); - - // Success - if (pingReply is { Status: IPStatus.Success }) + switch (ipAddress.AddressFamily) { - switch (ipAddress.AddressFamily) - { - case AddressFamily.InterNetwork: - return new PingInfo( - timestamp, - pingReply.Address, - pingReply.Buffer.Length, - pingReply.RoundtripTime, - pingReply.Options!.Ttl, - pingReply.Status); - case AddressFamily.InterNetworkV6: - return new PingInfo( + case AddressFamily.InterNetwork: + return new PingInfo( timestamp, pingReply.Address, pingReply.Buffer.Length, pingReply.RoundtripTime, + pingReply.Options!.Ttl, pingReply.Status); - } + case AddressFamily.InterNetworkV6: + return new PingInfo( + timestamp, + pingReply.Address, + pingReply.Buffer.Length, + pingReply.RoundtripTime, + pingReply.Status); } - - // Failed - if (pingReply != null) - return new PingInfo(timestamp, ipAddress, pingReply.Status); - } - catch (PingException) - { - // Ping failed with unknown status - return new PingInfo(timestamp, ipAddress, IPStatus.Unknown); } - // Don't scan again, if the user has canceled (when more than 1 attempt) - if (cancellationToken.IsCancellationRequested) - break; + // Failed + if (pingReply != null) + return new PingInfo(timestamp, ipAddress, pingReply.Status); + } + catch (PingException) + { + // Ping failed with unknown status + return new PingInfo(timestamp, ipAddress, IPStatus.Unknown); } - // Fall back to unknown status - return new PingInfo(DateTime.Now, ipAddress, IPStatus.Unknown); - }, cancellationToken); + // Don't scan again, if the user has canceled (when more than 1 attempt) + if (cancellationToken.IsCancellationRequested) + break; + } + + // Fall back to unknown status + return new PingInfo(DateTime.Now, ipAddress, IPStatus.Unknown); } - private Task> PortScanAsync(IPAddress ipAddress, ParallelOptions parallelOptions, + private async Task> PortScanAsync(IPAddress ipAddress, ParallelOptions parallelOptions, CancellationToken cancellationToken) { ConcurrentBag results = []; - Parallel.ForEach(options.PortScanPorts, parallelOptions, port => + await Parallel.ForEachAsync(options.PortScanPorts, parallelOptions, async (port, ct) => { - // Test if port is open - using var tcpClient = new TcpClient(ipAddress.AddressFamily); + var portState = await PortProbe.ProbeAsync(ipAddress, port, options.PortScanTimeout, ct) + .ConfigureAwait(false); - var portState = PortState.None; - - try - { - // ReSharper disable once MethodSupportsCancellation - Wait for timeout - var task = tcpClient.ConnectAsync(ipAddress, port); - - if (task.Wait(options.PortScanTimeout, cancellationToken)) - portState = tcpClient.Connected ? PortState.Open : PortState.Closed; - else - portState = PortState.TimedOut; - } - catch - { - portState = PortState.Closed; - } - finally - { - tcpClient.Close(); - - if (portState == PortState.Open || options.ShowAllResults) - results.Add( - new PortInfo(port, PortLookup.LookupByPortAndProtocol(port), portState)); - } - }); + if (portState == PortState.Open || options.ShowAllResults) + results.Add(new PortInfo(port, PortLookup.LookupByPortAndProtocol(port), portState)); + }).ConfigureAwait(false); - return Task.FromResult(results.AsEnumerable()); + return results.ToList(); } private void IncreaseProgress() diff --git a/Source/NETworkManager.Models/Network/NetBIOSResolver.cs b/Source/NETworkManager.Models/Network/NetBIOSResolver.cs index 342de111fe..a3cbc586c2 100644 --- a/Source/NETworkManager.Models/Network/NetBIOSResolver.cs +++ b/Source/NETworkManager.Models/Network/NetBIOSResolver.cs @@ -47,7 +47,6 @@ public static async Task ResolveAsync(IPAddress ipAddress, int time CancellationToken cancellationToken) { var udpClient = new UdpClient(); - udpClient.Client.ReceiveTimeout = timeout; var remoteEndPoint = new IPEndPoint(ipAddress, NetBIOSUdpPort); @@ -57,11 +56,17 @@ public static async Task ResolveAsync(IPAddress ipAddress, int time // ReSharper disable once MethodSupportsCancellation - cancellation is handled below by Task.WhenAny var receiveTask = udpClient.ReceiveAsync(); + var timeoutTask = Task.Delay(timeout, cancellationToken); - if (!receiveTask.Wait(timeout, cancellationToken)) + var completedTask = await Task.WhenAny(receiveTask, timeoutTask).ConfigureAwait(false); + + if (completedTask == timeoutTask) return new NetBIOSInfo(ipAddress); - var response = receiveTask.Result; + // Note: if the receive task were still pending here, it would fault once the socket + // is closed in the finally block below. Since we only reach this point when it has + // already completed, that's not a concern. + var response = await receiveTask.ConfigureAwait(false); if (response.Buffer.Length < ResponseBaseLen || response.Buffer[ResponseTypePos] != ResponseTypeNbstat) return new NetBIOSInfo(ipAddress); // response was too short diff --git a/Source/NETworkManager.Models/Network/PortProbe.cs b/Source/NETworkManager.Models/Network/PortProbe.cs new file mode 100644 index 0000000000..6e2096abba --- /dev/null +++ b/Source/NETworkManager.Models/Network/PortProbe.cs @@ -0,0 +1,53 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace NETworkManager.Models.Network; + +/// +/// Provides a shared, fully asynchronous TCP connect probe used by and +/// . +/// +internal static class PortProbe +{ + /// + /// Attempts a TCP connect to the given and and + /// classifies the result. Never throws for expected connect failures. + /// + /// IP address to connect to. + /// Port to connect to. + /// Timeout in milliseconds after which the port is considered timed out. + /// Token to monitor for cancellation requests. + /// The of the probed port. + public static async Task ProbeAsync(IPAddress ipAddress, int port, int timeoutMs, + CancellationToken cancellationToken) + { + using var tcpClient = new TcpClient(ipAddress.AddressFamily); + using var timeoutCts = new CancellationTokenSource(timeoutMs); + using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); + + try + { + await tcpClient.ConnectAsync(ipAddress, port, linkedCts.Token).ConfigureAwait(false); + + return tcpClient.Connected ? PortState.Open : PortState.Closed; + } + catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested && + !cancellationToken.IsCancellationRequested) + { + // Only our own timeout fired, not the caller's cancellation -> timed out + return PortState.TimedOut; + } + catch (Exception) when (!cancellationToken.IsCancellationRequested) + { + // Connection refused, host unreachable, etc. + return PortState.Closed; + } + finally + { + tcpClient.Close(); + } + } +} diff --git a/Source/NETworkManager.Models/Network/PortScanner.cs b/Source/NETworkManager.Models/Network/PortScanner.cs index 372cd2d861..2e12b0fec2 100644 --- a/Source/NETworkManager.Models/Network/PortScanner.cs +++ b/Source/NETworkManager.Models/Network/PortScanner.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Net; -using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using NETworkManager.Models.Lookup; @@ -67,7 +66,7 @@ public void ScanAsync(IEnumerable<(IPAddress ipAddress, string hostname)> hosts, { _progressValue = 0; - Task.Run(() => + Task.Run(async () => { try { @@ -83,58 +82,34 @@ public void ScanAsync(IEnumerable<(IPAddress ipAddress, string hostname)> hosts, MaxDegreeOfParallelism = _options.MaxPortThreads }; - Parallel.ForEach(hosts, hostParallelOptions, host => + await Parallel.ForEachAsync(hosts, hostParallelOptions, async (host, hostCt) => { // Resolve Hostname (PTR) var hostname = string.Empty; if (_options.ResolveHostname) { - // Don't use await in Parallel.ForEach, this will break - var dnsResolverTask = DNSClient.GetInstance().ResolvePtrAsync(host.ipAddress); + var dnsResult = await DNSClient.GetInstance().ResolvePtrAsync(host.ipAddress) + .ConfigureAwait(false); - // Wait for task inside a Parallel.Foreach - dnsResolverTask.Wait(cancellationToken); - - if (!dnsResolverTask.Result.HasError) - hostname = dnsResolverTask.Result.Value; + if (!dnsResult.HasError) + hostname = dnsResult.Value; } // Check each port - Parallel.ForEach(ports, portParallelOptions, port => + await Parallel.ForEachAsync(ports, portParallelOptions, async (port, portCt) => { - // Test if port is open - using (var tcpClient = new TcpClient(host.ipAddress.AddressFamily)) - { - var portState = PortState.None; - - try - { - var task = tcpClient.ConnectAsync(host.ipAddress, port); - - if (task.Wait(_options.Timeout)) - portState = tcpClient.Connected ? PortState.Open : PortState.Closed; - else - portState = PortState.TimedOut; - } - catch - { - portState = PortState.Closed; - } - finally - { - tcpClient.Close(); - - if (_options.ShowAllResults || portState == PortState.Open) - OnPortScanned(new PortScannerPortScannedArgs( - new PortScannerPortInfo(host.ipAddress, hostname, port, - PortLookup.LookupByPortAndProtocol(port), portState))); - } - } + var portState = await PortProbe.ProbeAsync(host.ipAddress, port, _options.Timeout, portCt) + .ConfigureAwait(false); + + if (_options.ShowAllResults || portState == PortState.Open) + OnPortScanned(new PortScannerPortScannedArgs( + new PortScannerPortInfo(host.ipAddress, hostname, port, + PortLookup.LookupByPortAndProtocol(port), portState))); IncreaseProgress(); - }); - }); + }).ConfigureAwait(false); + }).ConfigureAwait(false); } catch (OperationCanceledException) { diff --git a/Source/NETworkManager.Settings/GlobalStaticConfiguration.cs b/Source/NETworkManager.Settings/GlobalStaticConfiguration.cs index d71cb6aab6..360db3c0c5 100644 --- a/Source/NETworkManager.Settings/GlobalStaticConfiguration.cs +++ b/Source/NETworkManager.Settings/GlobalStaticConfiguration.cs @@ -61,7 +61,6 @@ public static class GlobalStaticConfiguration // Settings: General public static int General_BackgroundJobInterval => 5; - public static int General_ThreadPoolAdditionalMinThreads => 512; public static int General_HistoryListEntries => 10; // Settings: Window @@ -132,16 +131,16 @@ public static class GlobalStaticConfiguration public static int IPScanner_ICMPBuffer => 32; public static bool IPScanner_ResolveHostname => true; public static bool IPScanner_PortScanEnabled => true; - public static string IPScanner_PortScanPorts => "22; 53; 80; 139; 389; 636; 443; 445; 3389"; + public static string IPScanner_PortScanPorts => "22; 53; 80; 135; 139; 389; 636; 443; 445; 3389; 9100"; public static int IPScanner_PortScanTimeout => 4000; public static int IPScanner_MaxHostThreads => 256; - public static int IPScanner_MaxPortThreads => 5; + public static int IPScanner_MaxPortThreads => 4; public static bool IPScanner_NetBIOSEnabled => true; public static int IPScanner_NetBIOSTimeout => 4000; public static ExportFileType IPScanner_ExportFileType => ExportFileType.Csv; // Application: Port Scanner - public static int PortScanner_MaxHostThreads => 5; + public static int PortScanner_MaxHostThreads => 4; public static int PortScanner_MaxPortThreads => 256; public static int PortScanner_Timeout => 4000; public static ExportFileType PortScanner_ExportFileType => ExportFileType.Csv; diff --git a/Source/NETworkManager.Settings/SettingsInfo.cs b/Source/NETworkManager.Settings/SettingsInfo.cs index eb33ec907c..ae78719a25 100644 --- a/Source/NETworkManager.Settings/SettingsInfo.cs +++ b/Source/NETworkManager.Settings/SettingsInfo.cs @@ -152,20 +152,6 @@ public int General_BackgroundJobInterval } } = GlobalStaticConfiguration.General_BackgroundJobInterval; - - public int General_ThreadPoolAdditionalMinThreads - { - get; - set - { - if (value == field) - return; - - field = value; - OnPropertyChanged(); - } - } = GlobalStaticConfiguration.General_ThreadPoolAdditionalMinThreads; - public int General_HistoryListEntries { get; diff --git a/Source/NETworkManager/App.xaml.cs b/Source/NETworkManager/App.xaml.cs index 74ac6812a3..d041c1c105 100644 --- a/Source/NETworkManager/App.xaml.cs +++ b/Source/NETworkManager/App.xaml.cs @@ -165,27 +165,6 @@ by BornToBeRoot Log.Info("Background job is disabled."); } - // Setup ThreadPool for the application - ThreadPool.GetMaxThreads(out var workerThreadsMax, out var completionPortThreadsMax); - ThreadPool.GetMinThreads(out var workerThreadsMin, out var completionPortThreadsMin); - - var workerThreadsMinNew = workerThreadsMin + SettingsManager.Current.General_ThreadPoolAdditionalMinThreads; - var completionPortThreadsMinNew = completionPortThreadsMin + - SettingsManager.Current.General_ThreadPoolAdditionalMinThreads; - - if (workerThreadsMinNew > workerThreadsMax) - workerThreadsMinNew = workerThreadsMax; - - if (completionPortThreadsMinNew > completionPortThreadsMax) - completionPortThreadsMinNew = completionPortThreadsMax; - - if (ThreadPool.SetMinThreads(workerThreadsMinNew, completionPortThreadsMinNew)) - Log.Info( - $"ThreadPool min threads set to: workerThreads: {workerThreadsMinNew}, completionPortThreads: {completionPortThreadsMinNew}"); - else - Log.Warn( - $"ThreadPool min threads could not be set to workerThreads: {workerThreadsMinNew}, completionPortThreads: {completionPortThreadsMinNew}"); - // Show splash screen if (SettingsManager.Current.SplashScreen_Enabled) { diff --git a/Source/NETworkManager/ViewModels/SettingsGeneralViewModel.cs b/Source/NETworkManager/ViewModels/SettingsGeneralViewModel.cs index 300ef818ee..ddad5752de 100644 --- a/Source/NETworkManager/ViewModels/SettingsGeneralViewModel.cs +++ b/Source/NETworkManager/ViewModels/SettingsGeneralViewModel.cs @@ -45,22 +45,6 @@ public int BackgroundJobInterval } } - public int ThreadPoolAdditionalMinThreads - { - get; - set - { - if (value == field) - return; - - if (!_isLoading) - SettingsManager.Current.General_ThreadPoolAdditionalMinThreads = value; - - field = value; - OnPropertyChanged(); - } - } - public int HistoryListEntries { get; @@ -100,7 +84,6 @@ private void LoadSettings() }.View; BackgroundJobInterval = SettingsManager.Current.General_BackgroundJobInterval; - ThreadPoolAdditionalMinThreads = SettingsManager.Current.General_ThreadPoolAdditionalMinThreads; HistoryListEntries = SettingsManager.Current.General_HistoryListEntries; } diff --git a/Source/NETworkManager/Views/IPScannerSettingsView.xaml b/Source/NETworkManager/Views/IPScannerSettingsView.xaml index 3d3cc946a3..29b8164223 100644 --- a/Source/NETworkManager/Views/IPScannerSettingsView.xaml +++ b/Source/NETworkManager/Views/IPScannerSettingsView.xaml @@ -138,16 +138,5 @@ - - - - - - - - - \ No newline at end of file diff --git a/Source/NETworkManager/Views/PortScannerSettingsView.xaml b/Source/NETworkManager/Views/PortScannerSettingsView.xaml index 27077b6ac8..aaf35b0e34 100644 --- a/Source/NETworkManager/Views/PortScannerSettingsView.xaml +++ b/Source/NETworkManager/Views/PortScannerSettingsView.xaml @@ -103,16 +103,5 @@ - - - - - - - - - \ No newline at end of file diff --git a/Source/NETworkManager/Views/SettingsGeneralView.xaml b/Source/NETworkManager/Views/SettingsGeneralView.xaml index 6eec80259f..4215a2dc17 100644 --- a/Source/NETworkManager/Views/SettingsGeneralView.xaml +++ b/Source/NETworkManager/Views/SettingsGeneralView.xaml @@ -186,19 +186,6 @@ - - - - - - -