Skip to content

Commit 448b2af

Browse files
committed
feat: support newline-separated hosts and shorthand ranges
Host input (IP Scanner, Port Scanner, Ping Monitor) now accepts newline-separated entries, so a column pasted from Excel works directly (one IP/range per line). The existing semicolon-separated format keeps working. Shorthand IPv4 ranges like 192.168.0.1-100 (192.168.0.1 to 192.168.0.100) are now supported too, alongside the existing full-range format 192.168.0.1-192.168.0.100. - HostRangeHelper.CreateListFromInput: split on ';', CR, LF - HostRangeHelper.ResolveAsync: expand shorthand ranges - RegexHelper: add IPv4AddressShortRangeRegex - MultipleHostsRangeValidator: accept newlines + shorthand ranges - Docs: document both features
1 parent 1719c32 commit 448b2af

6 files changed

Lines changed: 66 additions & 10 deletions

File tree

Source/NETworkManager.Models/Network/HostRangeHelper.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using NETworkManager.Utilities;
2+
using System;
23
using System.Collections.Concurrent;
34
using System.Collections.Generic;
45
using System.Linq;
@@ -17,14 +18,15 @@ namespace NETworkManager.Models.Network;
1718
public static class HostRangeHelper
1819
{
1920
/// <summary>
20-
/// Create a list of hosts from a string input like "10.0.0.1; example.com; 10.0.0.0/24"
21+
/// Create a list of hosts from a string input like "10.0.0.1; example.com; 10.0.0.0/24".
22+
/// Inputs can also be separated by newlines (e.g. pasted from Excel, one host/range per line).
2123
/// </summary>
22-
/// <param name="hosts">Hosts like "10.0.0.1; example.com; 10.0.0.0/24"</param>
24+
/// <param name="hosts">Hosts like "10.0.0.1; example.com; 10.0.0.0/24" or newline-separated lines</param>
2325
/// <returns>List of hosts.</returns>
2426
public static IEnumerable<string> CreateListFromInput(string hosts)
2527
{
26-
return hosts.Replace(" ", "").Split(';')
27-
.Where(x => !string.IsNullOrEmpty(x))
28+
return hosts.Replace(" ", "")
29+
.Split([';', '\r', '\n'], StringSplitOptions.RemoveEmptyEntries)
2830
.Select(x => x.Trim())
2931
.ToArray();
3032
}
@@ -65,6 +67,22 @@ public static IEnumerable<string> CreateListFromInput(string hosts)
6567

6668
break;
6769

70+
// 192.168.0.1-100
71+
case var _ when RegexHelper.IPv4AddressShortRangeRegex().IsMatch(host):
72+
var shortRange = host.Split('-');
73+
var shortBase = shortRange[0][..shortRange[0].LastIndexOf('.')];
74+
75+
Parallel.For(IPv4Address.ToInt32(IPAddress.Parse(shortRange[0])),
76+
IPv4Address.ToInt32(IPAddress.Parse($"{shortBase}.{shortRange[1]}")) + 1, (i, state) =>
77+
{
78+
if (ct.IsCancellationRequested)
79+
state.Break();
80+
81+
hostsBag.Add((IPv4Address.FromInt32(i), string.Empty));
82+
});
83+
84+
break;
85+
6886
// 192.168.0.0 - 192.168.0.100
6987
case var _ when RegexHelper.IPv4AddressRangeRegex().IsMatch(host):
7088
var range = host.Split('-');

Source/NETworkManager.Utilities/RegexHelper.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,30 @@ public static partial class RegexHelper
4343
public static partial Regex IPv4AddressExtractRegex();
4444

4545
/// <summary>
46-
/// Provides a compiles regular expression that matches IPv4 address ranges in the format "start-end" like
46+
/// Represents a regular expression pattern that matches valid shorthand IPv4 address ranges like
47+
/// "192.168.178.1-100" (base IP + last octet range).
48+
/// </summary>
49+
private const string IPv4AddressShortRangeValues =
50+
@"((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]?)\-(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
51+
52+
/// <summary>
53+
/// Provides a compiled regular expression that matches IPv4 address ranges in the format "start-end" like
4754
/// "192.168.178.0-192.168.178.255".
48-
/// </summary>
55+
/// </summary>
4956
/// <returns>A <see cref="Regex"/> instance that matches strings representing IPv4 address ranges, such as
5057
/// "192.168.1.1-192.168.1.100".</returns>
5158
[GeneratedRegex($"^{IPv4AddressValues}-{IPv4AddressValues}$")]
5259
public static partial Regex IPv4AddressRangeRegex();
5360

61+
/// <summary>
62+
/// Provides a compiled regular expression that matches shorthand IPv4 address ranges like
63+
/// "192.168.178.1-100" (base IP followed by a last-octet range).
64+
/// </summary>
65+
/// <returns>A <see cref="Regex"/> instance that matches strings representing shorthand IPv4 address ranges,
66+
/// such as "192.168.1.1-100" (192.168.1.1 to 192.168.1.100).</returns>
67+
[GeneratedRegex($"^{IPv4AddressShortRangeValues}$")]
68+
public static partial Regex IPv4AddressShortRangeRegex();
69+
5470
/// <summary>
5571
/// Provides a compiled regular expression that matches valid IPv4 subnet mask like "255.255.0.0".
5672
/// </summary>

Source/NETworkManager.Validators/MultipleHostsRangeValidator.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using NETworkManager.Localization.Resources;
22
using NETworkManager.Models.Network;
33
using NETworkManager.Utilities;
4+
using System;
45
using System.DirectoryServices.ActiveDirectory;
56
using System.Globalization;
67
using System.Net;
@@ -18,7 +19,9 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo)
1819
if (value == null)
1920
return new ValidationResult(false, Strings.EnterValidIPScanRange);
2021

21-
foreach (var ipHostOrRange in ((string)value).Replace(" ", "").Split(';'))
22+
foreach (var ipHostOrRange in ((string)value)
23+
.Replace(" ", "")
24+
.Split([';', '\r', '\n'], StringSplitOptions.RemoveEmptyEntries))
2225
{
2326
// 192.168.0.1
2427
if (RegexHelper.IPv4AddressRegex().IsMatch(ipHostOrRange))
@@ -32,6 +35,19 @@ public override ValidationResult Validate(object value, CultureInfo cultureInfo)
3235
if (RegexHelper.IPv4AddressSubnetmaskRegex().IsMatch(ipHostOrRange))
3336
continue;
3437

38+
// 192.168.0.1-100
39+
if (RegexHelper.IPv4AddressShortRangeRegex().IsMatch(ipHostOrRange))
40+
{
41+
var shortRange = ipHostOrRange.Split('-');
42+
var shortBase = shortRange[0][..shortRange[0].LastIndexOf('.')];
43+
44+
if (IPv4Address.ToInt32(IPAddress.Parse(shortRange[0])) >
45+
IPv4Address.ToInt32(IPAddress.Parse($"{shortBase}.{shortRange[1]}")))
46+
isValid = false;
47+
48+
continue;
49+
}
50+
3551
// 192.168.0.0 - 192.168.0.100
3652
if (RegexHelper.IPv4AddressRangeRegex().IsMatch(ipHostOrRange))
3753
{

Website/docs/application/ip-scanner.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ With the **IP Scanner** you can scan for active devices based on the hostname or
3434

3535
:::note
3636

37-
Multiple inputs can be combined with a semicolon (`;`).
37+
Multiple inputs can be combined with a semicolon (`;`) or separated by newlines (one input per line, e.g. pasted from Excel).
3838

3939
Example: `10.0.0.0/24; 10.0.[10-20]1`
4040

41+
Shorthand ranges like `192.168.0.1-100` (192.168.0.1 to 192.168.0.100) are also supported.
42+
4143
:::
4244

4345
### Toolbar

Website/docs/application/ping-monitor.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ ICMP (Internet Control Message Protocol) is a network-layer protocol used to sen
3131

3232
:::note
3333

34-
Multiple inputs can be combined with a semicolon (`;`).
34+
Multiple inputs can be combined with a semicolon (`;`) or separated by newlines (one input per line, e.g. pasted from Excel).
3535

3636
Example: `10.0.0.0/24; 10.0.[10-20]1`
3737

38+
Shorthand ranges like `192.168.0.1-100` (192.168.0.1 to 192.168.0.100) are also supported.
39+
3840
:::
3941

4042
### Chart

Website/docs/application/port-scanner.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ TCP (Transmission Control Protocol) is a connection-oriented transport-layer pro
4545

4646
:::note
4747

48-
Multiple inputs can be combined with a semicolon (`;`).
48+
Multiple inputs can be combined with a semicolon (`;`) or separated by newlines (one input per line, e.g. pasted from Excel).
4949

5050
Example: `10.0.0.0/24; 10.0.[10-20]1` or `1-1024; 8080; 8443`
5151

52+
Shorthand ranges like `192.168.0.1-100` (192.168.0.1 to 192.168.0.100) are also supported.
53+
5254
:::
5355

5456
### Toolbar

0 commit comments

Comments
 (0)