Skip to content

Commit df47765

Browse files
BornToBeRootclaude
andcommitted
Fix: IP Scanner / Port Scanner / Ping Monitor: No message if all IP addresses are excluded and a hostname failed (R14)
The "all IP addresses are excluded" message was only shown if every hostname could be resolved - also an excluded hostname that could not be resolved suppressed it (e.g. "10.0.0.1; !10.0.0.1; !typo.lan"), and the Ping Monitor then cleared the input. HostRangeHelper.ResolveAsync returns the number of hosts removed by the exclusions; the message is shown if no host is left and something was excluded, after the "could not be resolved" message. The Ping Monitor keeps the input whenever no host is left. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 29806e9 commit df47765

5 files changed

Lines changed: 51 additions & 16 deletions

File tree

‎Source/NETworkManager.Models/Network/HostRangeHelper.cs‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,12 @@ private static long GetIPv4RangeCount(IPAddress start, IPAddress end)
227227
/// IP Scanner and the Port Scanner resolve the hostname themselves, a second entry would be scanned again).
228228
/// </param>
229229
/// <param name="cancellationToken">Token to monitor for cancellation requests.</param>
230-
/// <returns>Sorted, distinct list of hosts and the hostnames that could not be resolved.</returns>
231-
public static async Task<(List<(IPAddress ipAddress, string hostname)> hosts, List<string> hostnamesNotResolved)>
230+
/// <returns>
231+
/// Sorted, distinct list of hosts, the hostnames that could not be resolved and the number of hosts removed by
232+
/// the exclusions (e.g. to show that every IP address is excluded, also if a hostname could not be resolved).
233+
/// </returns>
234+
public static async Task<(List<(IPAddress ipAddress, string hostname)> hosts, List<string> hostnamesNotResolved,
235+
int excludedCount)>
232236
ResolveAsync(IEnumerable<string> hosts, bool dnsResolveHostnamePreferIPv4, bool keepAllHostnames,
233237
CancellationToken cancellationToken)
234238
{
@@ -350,11 +354,20 @@ await Parallel.ForEachAsync(Enumerable.Range(0, exclusionInputs.Count),
350354

351355
var activeExclusions = exclusions.Where(x => x != null).ToList();
352356

357+
var excludedCount = 0;
358+
353359
if (activeExclusions.Count > 0)
360+
{
361+
var count = result.Count;
362+
354363
result = [.. result.Where(host => !activeExclusions.Any(isExcluded => isExcluded(host.ipAddress)))];
355364

365+
excludedCount = count - result.Count;
366+
}
367+
356368
return (result,
357-
[.. hostnameNotResolvedPerInput.Concat(exclusionHostnameNotResolved).Where(x => x != null)]);
369+
[.. hostnameNotResolvedPerInput.Concat(exclusionHostnameNotResolved).Where(x => x != null)],
370+
excludedCount);
358371
}
359372

360373
/// <summary>

‎Source/NETworkManager.Tests/Network/HostRangeHelperExclusionTests.cs‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class HostRangeHelperExclusionTests
1010
{
1111
private static async Task<string[]> ResolveAsync(string input)
1212
{
13-
var (hosts, _) = await HostRangeHelper.ResolveAsync(HostRangeHelper.CreateListFromInput(input), true, false,
13+
var (hosts, _, _) = await HostRangeHelper.ResolveAsync(HostRangeHelper.CreateListFromInput(input), true, false,
1414
CancellationToken.None);
1515

1616
return [.. hosts.Select(x => x.ipAddress.ToString())];
@@ -38,6 +38,20 @@ public async Task ResolveAsync_OnlyExclusions_NoHosts()
3838
Assert.Empty(await ResolveAsync("!10.0.0.1"));
3939
}
4040

41+
[Theory]
42+
[InlineData("10.0.0.1; !10.0.0.1", 1)]
43+
[InlineData("10.0.0.0/30; !10.0.0.1-2", 2)]
44+
[InlineData("10.0.0.1-3; !10.0.0.0/8", 3)]
45+
[InlineData("10.0.0.1-3; !192.168.0.1", 0)]
46+
[InlineData("10.0.0.1-3", 0)]
47+
public async Task ResolveAsync_ExcludedCount(string input, int expected)
48+
{
49+
var (_, _, excludedCount) = await HostRangeHelper.ResolveAsync(HostRangeHelper.CreateListFromInput(input),
50+
true, false, CancellationToken.None);
51+
52+
Assert.Equal(expected, excludedCount);
53+
}
54+
4155
[Theory]
4256
[InlineData("10.0.0.0/24; !10.0.0.1", 256)]
4357
[InlineData("10.0.0.0/24; !10.0.0.0/8", 256)]

‎Source/NETworkManager/ViewModels/IPScannerViewModel.cs‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ private async Task Start()
857857

858858
// Resolve hostnames and convert the ports. Start() is fire-and-forget, so every exception has to be
859859
// handled here - otherwise the scan would never end (IsRunning stays true).
860-
(List<(IPAddress ipAddress, string hostname)> hosts, List<string> hostnamesNotResolved) hosts;
860+
(List<(IPAddress ipAddress, string hostname)> hosts, List<string> hostnamesNotResolved, int excludedCount) hosts;
861861
int[] ports;
862862
int[] webPagePorts;
863863

@@ -892,10 +892,12 @@ private async Task Start()
892892
IsStatusMessageDisplayed = true;
893893
}
894894

895-
// The input has an inclusion (validated) - without unresolved hostnames, the exclusions removed every host
896-
if (hosts.hosts.Count == 0 && hosts.hostnamesNotResolved.Count == 0)
895+
// The exclusions removed every host (also if hostnames could not be resolved, keep their message)
896+
if (hosts.hosts.Count == 0 && hosts.excludedCount > 0)
897897
{
898-
StatusMessage = Strings.AllIPAddressesAreExcludedMessage;
898+
StatusMessage = IsStatusMessageDisplayed
899+
? StatusMessage + Environment.NewLine + Strings.AllIPAddressesAreExcludedMessage
900+
: Strings.AllIPAddressesAreExcludedMessage;
899901
IsStatusMessageDisplayed = true;
900902
}
901903

‎Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ public async Task Start()
319319
_cancellationTokenSource = new CancellationTokenSource();
320320

321321
// Resolve hostnames
322-
(List<(IPAddress ipAddress, string hostname)> hosts, List<string> hostnamesNotResolved) hosts;
322+
(List<(IPAddress ipAddress, string hostname)> hosts, List<string> hostnamesNotResolved, int excludedCount) hosts;
323323

324324
try
325325
{
@@ -342,13 +342,19 @@ public async Task Start()
342342
IsStatusMessageDisplayed = true;
343343
}
344344

345-
// The input has an inclusion (validated) - without unresolved hostnames, the exclusions removed every host:
346-
// nothing to add, the input is kept to correct it
347-
if (hosts.hosts.Count == 0 && hosts.hostnamesNotResolved.Count == 0)
345+
// The exclusions removed every host (also if hostnames could not be resolved, keep their message)
346+
if (hosts.hosts.Count == 0 && hosts.excludedCount > 0)
348347
{
349-
StatusMessage = Strings.AllIPAddressesAreExcludedMessage;
348+
StatusMessage = IsStatusMessageDisplayed
349+
? StatusMessage + Environment.NewLine + Strings.AllIPAddressesAreExcludedMessage
350+
: Strings.AllIPAddressesAreExcludedMessage;
350351
IsStatusMessageDisplayed = true;
352+
}
351353

354+
// No host left (hostnames that could not be resolved, everything excluded): nothing to add, the input is kept
355+
// to correct it
356+
if (hosts.hosts.Count == 0)
357+
{
352358
IsRunning = false;
353359

354360
return;

‎Source/NETworkManager/ViewModels/PortScannerViewModel.cs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ private async Task Start()
920920

921921
// Resolve hostnames and convert the ports. Start() is fire-and-forget, so every exception has to be
922922
// handled here - otherwise the scan would never end (IsRunning stays true).
923-
(List<(IPAddress ipAddress, string hostname)> hosts, List<string> hostnamesNotResolved) hosts;
923+
(List<(IPAddress ipAddress, string hostname)> hosts, List<string> hostnamesNotResolved, int excludedCount) hosts;
924924
int[] ports;
925925

926926
try
@@ -947,8 +947,8 @@ private async Task Start()
947947
AddStatusMessage(
948948
$"{Strings.TheFollowingHostnamesCouldNotBeResolved} {string.Join(", ", hosts.hostnamesNotResolved)}");
949949

950-
// The input has an inclusion (validated) - without unresolved hostnames, the exclusions removed every host
951-
if (hosts.hosts.Count == 0 && hosts.hostnamesNotResolved.Count == 0)
950+
// The exclusions removed every host (also if hostnames could not be resolved)
951+
if (hosts.hosts.Count == 0 && hosts.excludedCount > 0)
952952
AddStatusMessage(Strings.AllIPAddressesAreExcludedMessage);
953953

954954
PortsToScan = ports.Length * hosts.hosts.Count;

0 commit comments

Comments
 (0)