Skip to content

Commit 00dace5

Browse files
dearmbclaude
andcommitted
feat: accept space/tab-separated host and ports in Port Scanner
A paired host can now be entered as "10.0.0.1 80" (or with a tab, e.g. two columns pasted from Excel) instead of only "10.0.0.1:80". The input is normalized to the canonical colon form before validation and parsing. Also hardened the paired regexes so a port group can never match empty (e.g. "10.0.0.1:-" or "10.0.0.1 123-" no longer match, which previously could crash port parsing). Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 8d47092 commit 00dace5

4 files changed

Lines changed: 30 additions & 3 deletions

File tree

‎Source/NETworkManager.Utilities/RegexHelper.cs‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public static partial class RegexHelper
7373
/// (multiple ports / ranges). Used by the Port Scanner to scan a specific host only for its own ports.
7474
/// </summary>
7575
private const string IPv4AddressWithPortValues =
76-
$@"{IPv4AddressValues}:({PortValues})(-{PortValues})?(,({PortValues})(-{PortValues})?)*";
76+
$@"{IPv4AddressValues}:(?=[0-9])({PortValues})(-(?=[0-9]){PortValues})?(,((?=[0-9]){PortValues})(-(?=[0-9]){PortValues})?)*";
7777

7878
/// <summary>
7979
/// Provides a compiled regular expression that matches an IPv4 address with ports like
@@ -83,6 +83,23 @@ public static partial class RegexHelper
8383
[GeneratedRegex($"^{IPv4AddressWithPortValues}$")]
8484
public static partial Regex IPv4AddressWithPortRegex();
8585

86+
/// <summary>
87+
/// Represents a regular expression pattern that matches an IPv4 address followed by one or more ports
88+
/// separated by whitespace instead of a colon, like "192.168.178.1 80" or "192.168.178.1&lt;TAB&gt;80,443-445".
89+
/// Such entries are converted into the canonical colon form ("192.168.178.1:80,443-445") so that pasting
90+
/// two columns from Excel (IP | port) works as well.
91+
/// </summary>
92+
private const string IPv4AddressWithWhitespaceSeparatedPortValues =
93+
$@"(?<ip>{IPv4AddressValues})[ \t]+(?=[0-9])(?<ports>({PortValues})(-(?=[0-9]){PortValues})?(,((?=[0-9]){PortValues})(-(?=[0-9]){PortValues})?)*)";
94+
95+
/// <summary>
96+
/// Provides a compiled regular expression that converts whitespace-separated paired hosts
97+
/// ("192.168.178.1 80") into the canonical colon form ("192.168.178.1:80").
98+
/// </summary>
99+
/// <returns>A <see cref="Regex"/> instance that matches IPv4 addresses with whitespace-separated ports.</returns>
100+
[GeneratedRegex(IPv4AddressWithWhitespaceSeparatedPortValues)]
101+
public static partial Regex IPv4AddressWithWhitespaceSeparatedPortRegex();
102+
86103
/// <summary>
87104
/// Provides a compiled regular expression that matches valid IPv4 subnet mask like "255.255.0.0".
88105
/// </summary>

‎Source/NETworkManager.Validators/MultipleHostsRangeValidator.cs‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo)
1919
if (value == null)
2020
return new ValidationResult(false, Strings.EnterValidIPScanRange);
2121

22-
foreach (var ipHostOrRange in ((string)value)
22+
// Normalize whitespace-separated paired hosts (e.g. "10.0.0.1 80" or two columns pasted from Excel)
23+
// into the canonical colon form "10.0.0.1:80" so they validate like colon-form paired hosts.
24+
var normalizedValue = RegexHelper.IPv4AddressWithWhitespaceSeparatedPortRegex()
25+
.Replace((string)value, "${ip}:${ports}");
26+
27+
foreach (var ipHostOrRange in normalizedValue
2328
.Replace(" ", "")
2429
.Split([';', '\r', '\n'], StringSplitOptions.RemoveEmptyEntries))
2530
{

‎Source/NETworkManager/ViewModels/PortScannerViewModel.cs‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,10 @@ private async Task Start()
417417

418418
// Separate paired hosts (e.g. "192.168.0.1:80,443") from regular hosts. Paired hosts are scanned only
419419
// for their own ports, regular hosts for the ports entered in the "Ports" field.
420-
var hostInputs = HostRangeHelper.CreateListFromInput(Host);
420+
// Normalize whitespace-separated paired hosts (e.g. "10.0.0.1 80" or two columns pasted from Excel)
421+
// into the canonical colon form before parsing.
422+
var hostInputs = HostRangeHelper.CreateListFromInput(
423+
RegexHelper.IPv4AddressWithWhitespaceSeparatedPortRegex().Replace(Host, "${ip}:${ports}"));
421424

422425
var pairedHostInputs = hostInputs
423426
.Where(x => RegexHelper.IPv4AddressWithPortRegex().IsMatch(x))

‎Website/docs/application/port-scanner.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ Shorthand ranges like `192.168.0.1-100` (192.168.0.1 to 192.168.0.100) are also
6161

6262
Such hosts are scanned **only** for the given ports, while regular hosts are scanned for the ports in the [Ports](#ports) field. You can mix both in a single scan, e.g. `10.0.0.1:80; 10.0.0.2; 10.0.0.3:22,3389` scans `10.0.0.1` for port `80`, `10.0.0.2` for the ports of the Ports field and `10.0.0.3` for ports `22` and `3389`. If all hosts use paired ports, the [Ports](#ports) field can be left empty.
6363

64+
Instead of a colon you can also separate a host and its ports with a space or tab — useful when copying two columns (IP | port) from Excel: `10.0.0.1 80` is treated the same as `10.0.0.1:80`.
65+
6466
:::
6567

6668
### Toolbar

0 commit comments

Comments
 (0)