From 01ae7e68aaddf01c964f0580bfeefcfcdceaff8e Mon Sep 17 00:00:00 2001
From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com>
Date: Sat, 9 Dec 2023 01:33:44 +0100
Subject: [PATCH] Feature: Ping Monitor improvements (#2573)
* Feature: Ping Monitor improvements
* Feature: DNS Lookup / Port Scanner improve group string
* Feature: Host range aded to Ping Monitor & new design
* Feature: Allow range in profile
* Feature: Ping Monitor
* Feature: Setting to expand host view
* Feature: Expand ping monitor host
* Docs: Ping Monitor
* Chore: Refactoring
* Feature: Ping Monitor
* Docs: Update img
---
InnoSetup.iss | 2 +-
Source/GlobalAssemblyInfo.cs | 4 +-
.../PercentConverter.cs | 21 -
.../Resources/StaticStrings.Designer.cs | 2 +-
.../Resources/StaticStrings.resx | 2 +-
.../Resources/Strings.Designer.cs | 29 +-
.../Resources/Strings.resx | 11 +-
.../ExportManager.DNSLookupRecordInfo.cs | 14 +-
.../Network/DNSLookup.cs | 33 +-
.../Network/DNSLookupRecordInfo.cs | 39 +-
.../Network/HostNotFoundException.cs | 21 -
.../Network/HostRangeHelper.cs | 195 +++++-----
.../Network/HostnameArgs.cs | 23 ++
.../Network/IPScanner.cs | 24 +-
Source/NETworkManager.Models/Network/Ping.cs | 25 +-
.../Network/PingMonitorOptions.cs | 15 -
.../Network/PortScanner.cs | 15 +-
.../Network/PortScannerPortInfo.cs | 5 +
.../Network/Traceroute.cs | 12 +-
.../GlobalStaticConfiguration.cs | 1 +
.../NETworkManager.Settings/SettingsInfo.cs | 17 +-
.../SettingsManager.cs | 20 +-
Source/NETworkManager.Utilities/DNSClient.cs | 47 +--
.../IPAddressComparer.cs | 13 +
.../IPAddressHelper.cs | 24 ++
Source/NETworkManager/ProfileDialogManager.cs | 34 +-
.../Resources/Styles/ListBoxStyle.xaml | 8 -
.../ViewModels/DNSLookupHostViewModel.cs | 6 +-
.../ViewModels/DNSLookupViewModel.cs | 6 +-
.../ViewModels/IPScannerViewModel.cs | 126 +++---
.../ViewModels/PingMonitorHostViewModel.cs | 250 +++++++-----
.../PingMonitorSettingsViewModel.cs | 18 +
.../ViewModels/PingMonitorViewModel.cs | 155 +++++---
.../ViewModels/PortScannerViewModel.cs | 119 +++---
.../Views/DiscoveryProtocolView.xaml | 31 +-
.../NETworkManager/Views/IPScannerView.xaml | 30 +-
.../Views/IPScannerView.xaml.cs | 2 +-
.../Views/PingMonitorHostView.xaml | 107 +++++-
.../Views/PingMonitorHostView.xaml.cs | 5 +-
.../Views/PingMonitorSettingsView.xaml | 4 +-
.../NETworkManager/Views/PingMonitorView.xaml | 359 ++++++++++--------
.../Views/PingMonitorView.xaml.cs | 21 +-
.../NETworkManager/Views/PortScannerView.xaml | 16 +-
.../Views/PortScannerView.xaml.cs | 2 +-
.../NETworkManager/Views/ProfileDialog.xaml | 4 +-
Source/NETworkManager/Views/WhoisView.xaml | 9 +-
docs/Changelog/next-release.md | 14 +
.../01_Application/06_PingMonitor.md | 22 +-
.../01_Application/06_PingMonitor.png | Bin 52304 -> 93045 bytes
49 files changed, 1161 insertions(+), 801 deletions(-)
delete mode 100644 Source/NETworkManager.Converters/PercentConverter.cs
delete mode 100644 Source/NETworkManager.Models/Network/HostNotFoundException.cs
create mode 100644 Source/NETworkManager.Models/Network/HostnameArgs.cs
delete mode 100644 Source/NETworkManager.Models/Network/PingMonitorOptions.cs
create mode 100644 Source/NETworkManager.Utilities/IPAddressComparer.cs
diff --git a/InnoSetup.iss b/InnoSetup.iss
index 9b8146e3f6..9b37a6a418 100644
--- a/InnoSetup.iss
+++ b/InnoSetup.iss
@@ -2,7 +2,7 @@
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "NETworkManager"
-#define MyAppVersion "2023.11.28.0"
+#define MyAppVersion "2023.12.9.0"
#define MyAppPublisher "BornToBeRoot"
#define MyAppURL "https://github.com/BornToBeRoot/NETworkManager/"
#define MyAppExeName "NETworkManager.exe"
diff --git a/Source/GlobalAssemblyInfo.cs b/Source/GlobalAssemblyInfo.cs
index 9f8aa2cbe3..42739fc2cb 100644
--- a/Source/GlobalAssemblyInfo.cs
+++ b/Source/GlobalAssemblyInfo.cs
@@ -6,5 +6,5 @@
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("2023.11.28.0")]
-[assembly: AssemblyFileVersion("2023.11.28.0")]
+[assembly: AssemblyVersion("2023.12.9.0")]
+[assembly: AssemblyFileVersion("2023.12.9.0")]
diff --git a/Source/NETworkManager.Converters/PercentConverter.cs b/Source/NETworkManager.Converters/PercentConverter.cs
deleted file mode 100644
index 3dada7e353..0000000000
--- a/Source/NETworkManager.Converters/PercentConverter.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.Globalization;
-using System.Windows.Data;
-
-namespace NETworkManager.Converters;
-
-public sealed class PercentConverter : IMultiValueConverter
-{
- public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
- {
- if ((int)values[0] == 0)
- return 0;
-
- return Math.Round(((float)((int)values[1]) / (int)values[0]) * 100, 2);
- }
-
- public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
- {
- throw new NotImplementedException();
- }
-}
diff --git a/Source/NETworkManager.Localization/Resources/StaticStrings.Designer.cs b/Source/NETworkManager.Localization/Resources/StaticStrings.Designer.cs
index 86ac9ca1b1..d324a9d6a5 100644
--- a/Source/NETworkManager.Localization/Resources/StaticStrings.Designer.cs
+++ b/Source/NETworkManager.Localization/Resources/StaticStrings.Designer.cs
@@ -222,7 +222,7 @@ public static string ExampleHostnameOrIPAddress {
}
///
- /// Looks up a localized string similar to 192.168.1.0/24; 192.168.178.1 - 192.168.178.128; 192.168.[178-179].[1,100,150-200]; borntoberoot.net/24.
+ /// Looks up a localized string similar to 192.168.178.0/24; 10.0.0.0 - 10.0.0.9; 10.0.[0-9,20].[1-2]; server-01.borntoberoot.net/24.
///
public static string ExampleHostRange {
get {
diff --git a/Source/NETworkManager.Localization/Resources/StaticStrings.resx b/Source/NETworkManager.Localization/Resources/StaticStrings.resx
index f3f3400d9e..c8dc4731f0 100644
--- a/Source/NETworkManager.Localization/Resources/StaticStrings.resx
+++ b/Source/NETworkManager.Localization/Resources/StaticStrings.resx
@@ -145,7 +145,7 @@
SERVER-01 or 10.0.0.10
- 192.168.1.0/24; 192.168.178.1 - 192.168.178.128; 192.168.[178-179].[1,100,150-200]; borntoberoot.net/24
+ 192.168.178.0/24; 10.0.0.0 - 10.0.0.9; 10.0.[0-9,20].[1-2]; server-01.borntoberoot.net/24
10.0.0.10
diff --git a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
index 79e13af035..527a01772c 100644
--- a/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
+++ b/Source/NETworkManager.Localization/Resources/Strings.Designer.cs
@@ -1391,6 +1391,15 @@ public static string CanceledByUserMessage {
}
}
+ ///
+ /// Looks up a localized string similar to Host cannot be set while other hosts are being added. Please wait until the process is complete and try again..
+ ///
+ public static string CannotSetHostWhileRunningMessage {
+ get {
+ return ResourceManager.GetString("CannotSetHostWhileRunningMessage", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Caps lock is enabled!.
///
@@ -1661,6 +1670,15 @@ public static string Close {
}
}
+ ///
+ /// Looks up a localized string similar to Close all.
+ ///
+ public static string CloseAll {
+ get {
+ return ResourceManager.GetString("CloseAll", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Closed.
///
@@ -3607,6 +3625,15 @@ public static string Expand {
}
}
+ ///
+ /// Looks up a localized string similar to Expand host view.
+ ///
+ public static string ExpandHostView {
+ get {
+ return ResourceManager.GetString("ExpandHostView", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Experience.
///
@@ -4387,7 +4414,7 @@ public static string HostnameOrIPAddress {
}
///
- /// Looks up a localized string similar to Host(s).
+ /// Looks up a localized string similar to Hosts.
///
public static string Hosts {
get {
diff --git a/Source/NETworkManager.Localization/Resources/Strings.resx b/Source/NETworkManager.Localization/Resources/Strings.resx
index 9a3907e955..71d346647f 100644
--- a/Source/NETworkManager.Localization/Resources/Strings.resx
+++ b/Source/NETworkManager.Localization/Resources/Strings.resx
@@ -873,7 +873,7 @@ First make a backup copy of your profile files before enabling encryption!Hostname or IP address
- Host(s)
+ Hosts
ID
@@ -3748,4 +3748,13 @@ Try again in a few seconds.
The object's state is inconsistent, preventing the set operation!
+
+ Close all
+
+
+ Expand host view
+
+
+ Host cannot be set while other hosts are being added. Please wait until the process is complete and try again.
+
\ No newline at end of file
diff --git a/Source/NETworkManager.Models/Export/ExportManager.DNSLookupRecordInfo.cs b/Source/NETworkManager.Models/Export/ExportManager.DNSLookupRecordInfo.cs
index f60bab636d..60db155e11 100644
--- a/Source/NETworkManager.Models/Export/ExportManager.DNSLookupRecordInfo.cs
+++ b/Source/NETworkManager.Models/Export/ExportManager.DNSLookupRecordInfo.cs
@@ -46,11 +46,11 @@ private static void CreateCsv(IEnumerable collection, strin
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine(
- $"{nameof(DNSLookupRecordInfo.DomainName)},{nameof(DNSLookupRecordInfo.TTL)},{nameof(DNSLookupRecordInfo.RecordClass)},{nameof(DNSLookupRecordInfo.RecordType)},{nameof(DNSLookupRecordInfo.Result)},{nameof(DNSLookupRecordInfo.Server)},{nameof(DNSLookupRecordInfo.IPEndPoint)}");
+ $"{nameof(DNSLookupRecordInfo.DomainName)},{nameof(DNSLookupRecordInfo.TTL)},{nameof(DNSLookupRecordInfo.RecordClass)},{nameof(DNSLookupRecordInfo.RecordType)},{nameof(DNSLookupRecordInfo.Result)},{nameof(DNSLookupRecordInfo.NameServerIPAddress)},{nameof(DNSLookupRecordInfo.NameServerHostName)},{nameof(DNSLookupRecordInfo.NameServerPort)}");
foreach (var info in collection)
stringBuilder.AppendLine(
- $"{info.DomainName},{info.TTL},{info.RecordClass},{info.RecordType},{info.Result},{info.Server},{info.IPEndPoint}");
+ $"{info.DomainName},{info.TTL},{info.RecordClass},{info.RecordType},{info.Result},{info.NameServerIPAddress},{info.NameServerHostName},{info.NameServerPort}");
System.IO.File.WriteAllText(filePath, stringBuilder.ToString());
}
@@ -73,8 +73,9 @@ from info in collection
new XElement(nameof(DNSLookupRecordInfo.RecordClass), info.RecordClass),
new XElement(nameof(DNSLookupRecordInfo.RecordType), info.RecordType),
new XElement(nameof(DNSLookupRecordInfo.Result), info.Result),
- new XElement(nameof(DNSLookupRecordInfo.Server), info.Server),
- new XElement(nameof(DNSLookupRecordInfo.IPEndPoint), info.IPEndPoint)))));
+ new XElement(nameof(DNSLookupRecordInfo.NameServerIPAddress), info.NameServerIPAddress),
+ new XElement(nameof(DNSLookupRecordInfo.NameServerHostName), info.NameServerHostName),
+ new XElement(nameof(DNSLookupRecordInfo.NameServerPort), info.NameServerPort)))));
document.Save(filePath);
}
@@ -97,8 +98,9 @@ private static void CreateJson(IReadOnlyList collection, st
collection[i].RecordClass,
collection[i].RecordType,
collection[i].Result,
- collection[i].Server,
- collection[i].IPEndPoint
+ collection[i].NameServerIPAddress,
+ collection[i].NameServerHostName,
+ collection[i].NameServerPort
};
}
diff --git a/Source/NETworkManager.Models/Network/DNSLookup.cs b/Source/NETworkManager.Models/Network/DNSLookup.cs
index 9cf248a9ae..7115c8b680 100644
--- a/Source/NETworkManager.Models/Network/DNSLookup.cs
+++ b/Source/NETworkManager.Models/Network/DNSLookup.cs
@@ -2,11 +2,11 @@
using DnsClient.Protocol;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
+using NETworkManager.Utilities;
namespace NETworkManager.Models.Network;
@@ -116,8 +116,15 @@ public void ResolveAsync(IEnumerable hosts)
var queries = _addSuffix && _settings.QueryType != QueryType.PTR ? GetHostWithSuffix(hosts) : hosts;
// Foreach dns server
- Parallel.ForEach(_servers, dnsServer =>
+ Parallel.ForEach(_servers, async dnsServer =>
{
+ // Get the dns server hostname for some additional information
+ var dnsServerHostName = string.Empty;
+
+ var dnsResult = await DNSClient.GetInstance().ResolvePtrAsync(dnsServer.Address);
+ if (!dnsResult.HasError)
+ dnsServerHostName = dnsResult.Value;
+
// Init each dns server once
LookupClientOptions lookupClientOptions = new(dnsServer)
{
@@ -129,7 +136,7 @@ public void ResolveAsync(IEnumerable hosts)
};
LookupClient lookupClient = new(lookupClientOptions);
-
+
// Foreach host
Parallel.ForEach(queries, query =>
{
@@ -153,7 +160,7 @@ public void ResolveAsync(IEnumerable hosts)
}
// Process the results...
- ProcessDnsAnswers(dnsResponse.Answers, dnsResponse.NameServer);
+ ProcessDnsAnswers(dnsResponse.Answers, dnsResponse.NameServer, dnsServerHostName);
}
catch (Exception ex)
{
@@ -171,7 +178,7 @@ public void ResolveAsync(IEnumerable hosts)
///
/// List of DNS resource records.
/// DNS name server that answered the query.
- private void ProcessDnsAnswers(IEnumerable answers, NameServer nameServer)
+ private void ProcessDnsAnswers(IEnumerable answers, NameServer nameServer, string nameServerHostname = null)
{
if(answers is not DnsResourceRecord[] dnsResourceRecords)
return;
@@ -180,49 +187,49 @@ private void ProcessDnsAnswers(IEnumerable answers, NameServe
foreach (var record in dnsResourceRecords.ARecords())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
- record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}" , $"{record.Address}", $"{nameServer.Address}", $"{nameServer.Address}:{nameServer.Port}")));
+ record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}" , $"{record.Address}", $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// AAAA
foreach (var record in dnsResourceRecords.AaaaRecords())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
- record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", $"{record.Address}", $"{nameServer.Address}", $"{nameServer.Address}:{nameServer.Port}")));
+ record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", $"{record.Address}", $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// CNAME
foreach (var record in dnsResourceRecords.CnameRecords())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
- record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", record.CanonicalName, $"{nameServer.Address}", $"{nameServer.Address}:{nameServer.Port}")));
+ record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", record.CanonicalName, $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// MX
foreach (var record in dnsResourceRecords.MxRecords())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
- record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", record.Exchange, $"{nameServer.Address}", $"{nameServer.Address}:{nameServer.Port}")));
+ record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", record.Exchange, $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// NS
foreach (var record in dnsResourceRecords.NsRecords())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
- record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", record.NSDName, $"{nameServer.Address}", $"{nameServer.Address}:{nameServer.Port}")));
+ record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", record.NSDName, $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// PTR
foreach (var record in dnsResourceRecords.PtrRecords())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
- record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", record.PtrDomainName, $"{nameServer.Address}", $"{nameServer.Address}:{nameServer.Port}")));
+ record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", record.PtrDomainName, $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// SOA
foreach (var record in dnsResourceRecords.SoaRecords())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
- record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", record.MName + ", " + record.RName, $"{nameServer.Address}", $"{nameServer.Address}:{nameServer.Port}")));
+ record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", record.MName + ", " + record.RName, $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// TXT
foreach (var record in dnsResourceRecords.TxtRecords())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
- record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", string.Join(", ", record.Text), $"{nameServer.Address}", $"{nameServer.Address}:{nameServer.Port}")));
+ record.DomainName, record.TimeToLive, $"{record.RecordClass}",$"{record.RecordType}", string.Join(", ", record.Text), $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// ToDo: implement more
}
diff --git a/Source/NETworkManager.Models/Network/DNSLookupRecordInfo.cs b/Source/NETworkManager.Models/Network/DNSLookupRecordInfo.cs
index dfe9521f0a..3b354129cc 100644
--- a/Source/NETworkManager.Models/Network/DNSLookupRecordInfo.cs
+++ b/Source/NETworkManager.Models/Network/DNSLookupRecordInfo.cs
@@ -9,12 +9,12 @@ public class DNSLookupRecordInfo
/// Domain name of the record.
///
public string DomainName { get; set; }
-
+
///
/// Time to live (TTL) of the record.
///
public int TTL { get; set; }
-
+
///
/// Class of the record.
///
@@ -31,14 +31,26 @@ public class DNSLookupRecordInfo
public string Result { get; set; }
///
- /// Name server that provided the result.
+ /// IP address of the name server that provided the result.
+ ///
+ public string NameServerIPAddress { get; set; }
+
+ ///
+ /// Port of the name server that provided the result.
+ ///
+ public int NameServerPort { get; set; }
+
+ ///
+ /// Hostname of the name server that provided the result.
///
- public string Server { get; set; }
+ public string NameServerHostName { get; set; }
///
- /// IP endpoint (IP address:port) of the name server that provided the result.
+ /// Hostname (if available) or/and IP address with port of the name server that provided the result.
///
- public string IPEndPoint { get; set; }
+ public string NameServerAsString => string.IsNullOrEmpty(NameServerHostName)
+ ? $"{NameServerIPAddress}:{NameServerPort}"
+ : $"{NameServerHostName.TrimEnd('.')} # {NameServerIPAddress}:{NameServerPort}";
///
/// Creates a new instance of with the specified parameters.
@@ -48,16 +60,19 @@ public class DNSLookupRecordInfo
/// Class of the record.
/// Type of the record.
/// Result of the record. (IP address, hostname, text, etc.)
- /// Name server that provided the result.
- /// IP endpoint (IP address:port) of the name server that provided the result.
- public DNSLookupRecordInfo(string domainName, int ttl, string recordClass, string recordType, string result, string server, string ipEndPoint)
+ /// IP address of the name server that provided the result.
+ /// Hostname of the name server that provided the result.
+ /// Port of the name server that provided the result.
+ public DNSLookupRecordInfo(string domainName, int ttl, string recordClass, string recordType, string result,
+ string nameServerIPAddress, string nameServerHostName, int nameServerPort)
{
DomainName = domainName;
TTL = ttl;
RecordClass = recordClass;
RecordType = recordType;
Result = result;
- Server = server;
- IPEndPoint = ipEndPoint;
+ NameServerIPAddress = nameServerIPAddress;
+ NameServerPort = nameServerPort;
+ NameServerHostName = nameServerHostName;
}
-}
+}
\ No newline at end of file
diff --git a/Source/NETworkManager.Models/Network/HostNotFoundException.cs b/Source/NETworkManager.Models/Network/HostNotFoundException.cs
deleted file mode 100644
index ad3f8999a5..0000000000
--- a/Source/NETworkManager.Models/Network/HostNotFoundException.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-
-namespace NETworkManager.Models.Network;
-
-public class HostNotFoundException : Exception
-{
- public HostNotFoundException()
- {
-
- }
-
- public HostNotFoundException(string message) : base(message)
- {
-
- }
-
- public HostNotFoundException(string message, Exception innerException) : base(message, innerException)
- {
-
- }
-}
diff --git a/Source/NETworkManager.Models/Network/HostRangeHelper.cs b/Source/NETworkManager.Models/Network/HostRangeHelper.cs
index 8dad0d15aa..91c971fff4 100644
--- a/Source/NETworkManager.Models/Network/HostRangeHelper.cs
+++ b/Source/NETworkManager.Models/Network/HostRangeHelper.cs
@@ -13,67 +13,87 @@
namespace NETworkManager.Models.Network;
+///
+/// Helper class to interact with host ranges.
+/// E.g. Parse inputs, resolve hostnames and ip ranges.
+///
public static class HostRangeHelper
{
- public static Task CreateIPAddressesFromIPRangesAsync(string[] ipRanges, CancellationToken cancellationToken)
+ ///
+ /// Create a list of hosts from a string input like "10.0.0.1; example.com; 10.0.0.0/24"
+ ///
+ /// Hosts like "10.0.0.1; example.com; 10.0.0.0/24"
+ /// List of hosts.
+ public static IEnumerable CreateListFromInput(string hosts)
{
- return Task.Run(() => CreateIPAddressesFromIPRanges(ipRanges, cancellationToken), cancellationToken);
+ return hosts.Replace(" ", "").Split(';')
+ .Where(x => !string.IsNullOrEmpty(x))
+ .Select(x => x.Trim())
+ .ToArray();
}
- public static IPAddress[] CreateIPAddressesFromIPRanges(string[] ipRanges, CancellationToken cancellationToken)
+ public static Task<(List<(IPAddress ipAddress, string hostname)> hosts, List hostnamesNotResolved)> ResolveAsync(IEnumerable hosts, bool dnsResolveHostnamePreferIPv4, CancellationToken cancellationToken)
{
- var bag = new ConcurrentBag();
+ return Task.Run(() => Resolve(hosts, dnsResolveHostnamePreferIPv4, cancellationToken), cancellationToken);
+ }
- var parallelOptions = new ParallelOptions
- {
- 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();
- foreach (var ipOrRange in ipRanges)
+ Parallel.ForEach(hosts, new ParallelOptions { CancellationToken = cancellationToken }, host =>
{
- switch (ipOrRange)
+ switch (host)
{
- // 192.168.0.1 or 2001:db8:85a3::8a2e:370:7334
- case var _ when Regex.IsMatch(ipOrRange, RegexHelper.IPv4AddressRegex) || Regex.IsMatch(ipOrRange, RegexHelper.IPv6AddressRegex):
- bag.Add(IPAddress.Parse(ipOrRange));
+ // 192.168.0.1
+ case var _ when Regex.IsMatch(host, RegexHelper.IPv4AddressRegex):
+ // 2001:db8:85a3::8a2e:370:7334
+ case var _ when Regex.IsMatch(host, RegexHelper.IPv6AddressRegex):
+ hostsBag.Add((IPAddress.Parse(host), string.Empty));
+
break;
+
+ // 192.168.0.0/24
+ case var _ when Regex.IsMatch(host, RegexHelper.IPv4AddressCidrRegex):
+ // 192.168.0.0/255.255.255.0
+ case var _ when Regex.IsMatch(host, RegexHelper.IPv4AddressSubnetmaskRegex):
+ var network = IPNetwork2.System.Net.IPNetwork.Parse(host);
- // 192.168.0.0/24 or 192.168.0.0/255.255.255.0
- case var _ when Regex.IsMatch(ipOrRange, RegexHelper.IPv4AddressCidrRegex) || Regex.IsMatch(ipOrRange, RegexHelper.IPv4AddressSubnetmaskRegex):
- var network = IPNetwork2.System.Net.IPNetwork.Parse(ipOrRange);
-
- Parallel.For(IPv4Address.ToInt32(network.Network), IPv4Address.ToInt32(network.Broadcast) + 1, parallelOptions, i =>
+ Parallel.For(IPv4Address.ToInt32(network.Network), IPv4Address.ToInt32(network.Broadcast) + 1, (i, state) =>
{
- bag.Add(IPv4Address.FromInt32(i));
+ if (cancellationToken.IsCancellationRequested)
+ state.Break();
- parallelOptions.CancellationToken.ThrowIfCancellationRequested();
+ hostsBag.Add((IPv4Address.FromInt32(i), string.Empty));
});
-
+
break;
+
+ // 192.168.0.0 - 192.168.0.100
+ case var _ when Regex.IsMatch(host, RegexHelper.IPv4AddressRangeRegex):
+ var range = host.Split('-');
- // 192.168.0.0 - 192.168.0.100
- case var _ when Regex.IsMatch(ipOrRange, RegexHelper.IPv4AddressRangeRegex):
- var range = ipOrRange.Split('-');
-
- Parallel.For(IPv4Address.ToInt32(IPAddress.Parse(range[0])), IPv4Address.ToInt32(IPAddress.Parse(range[1])) + 1, parallelOptions, i =>
+ Parallel.For(IPv4Address.ToInt32(IPAddress.Parse(range[0])), IPv4Address.ToInt32(IPAddress.Parse(range[1])) + 1, (i, state) =>
{
- bag.Add(IPv4Address.FromInt32(i));
+ if (cancellationToken.IsCancellationRequested)
+ state.Break();
- parallelOptions.CancellationToken.ThrowIfCancellationRequested();
+ hostsBag.Add((IPv4Address.FromInt32(i), string.Empty));
});
break;
+
+ // 192.168.[50-100].1
+ case var _ when Regex.IsMatch(host, RegexHelper.IPv4AddressSpecialRangeRegex):
+ var octets = host.Split('.');
- // 192.168.[50-100,200].1 --> 192.168.50.1, 192.168.51.1, 192.168.52.1, {..}, 192.168.200.1
- case var _ when Regex.IsMatch(ipOrRange, RegexHelper.IPv4AddressSpecialRangeRegex):
- var octets = ipOrRange.Split('.');
-
- var list = new List>();
+ var list = new List>();
// Go through each octet...
foreach (var octet in octets)
{
- var innerList = new List();
+ var innerList = new ConcurrentBag();
// Create a range for each octet
if (Regex.IsMatch(octet, RegexHelper.SpecialRangeRegex))
@@ -85,10 +105,13 @@ public static IPAddress[] CreateIPAddressesFromIPRanges(string[] ipRanges, Cance
{
var rangeNumbers = numberOrRange.Split('-');
- for (var i = int.Parse(rangeNumbers[0]); i < (int.Parse(rangeNumbers[1]) + 1); i++)
+ Parallel.For(int.Parse(rangeNumbers[0]), int.Parse(rangeNumbers[1]) + 1, (i, state) =>
{
+ if (cancellationToken.IsCancellationRequested)
+ state.Break();
+
innerList.Add(i);
- }
+ });
} // 200
else
{
@@ -105,101 +128,81 @@ public static IPAddress[] CreateIPAddressesFromIPRanges(string[] ipRanges, Cance
}
// Build the new ipv4
- foreach (var i in list[0])
+ Parallel.ForEach(list[0], new ParallelOptions { CancellationToken = cancellationToken }, i =>
{
- foreach (var j in list[1])
+ Parallel.ForEach(list[1], new ParallelOptions { CancellationToken = cancellationToken }, j =>
{
- foreach (var k in list[2])
+ Parallel.ForEach(list[2], new ParallelOptions { CancellationToken = cancellationToken }, k =>
{
- foreach (var h in list[3])
+ Parallel.ForEach(list[3], new ParallelOptions { CancellationToken = cancellationToken }, h =>
{
- bag.Add(IPAddress.Parse($"{i}.{j}.{k}.{h}"));
- }
- }
- }
- }
+ hostsBag.Add((IPAddress.Parse($"{i}.{j}.{k}.{h}"), string.Empty));
+ });
+ });
+ });
+ });
break;
- }
- }
-
- return bag.ToArray();
- }
- public static Task> ResolveHostnamesInIPRangesAsync(string[] ipRanges, bool dnsResolveHostnamePreferIPv4, CancellationToken cancellationToken)
- {
- return Task.Run(() => ResolveHostnamesInIPRanges(ipRanges, dnsResolveHostnamePreferIPv4, cancellationToken), cancellationToken);
- }
-
- public static List ResolveHostnamesInIPRanges(string[] ipRanges, bool dnsResolveHostnamePreferIPv4, CancellationToken cancellationToken)
- {
- var bag = new ConcurrentBag();
-
- var exceptions = new ConcurrentQueue();
-
- Parallel.ForEach(ipRanges, new ParallelOptions { CancellationToken = cancellationToken }, ipHostOrRange =>
- {
- switch (ipHostOrRange)
- {
- // 192.168.0.1
- case var _ when Regex.IsMatch(ipHostOrRange, RegexHelper.IPv4AddressRegex):
- // 192.168.0.0/24
- case var _ when Regex.IsMatch(ipHostOrRange, RegexHelper.IPv4AddressCidrRegex):
- // 192.168.0.0/255.255.255.0
- case var _ when Regex.IsMatch(ipHostOrRange, RegexHelper.IPv4AddressSubnetmaskRegex):
- // 192.168.0.0 - 192.168.0.100
- case var _ when Regex.IsMatch(ipHostOrRange, RegexHelper.IPv4AddressRangeRegex):
- // 192.168.[50-100].1
- case var _ when Regex.IsMatch(ipHostOrRange, RegexHelper.IPv4AddressSpecialRangeRegex):
- // 2001:db8:85a3::8a2e:370:7334
- case var _ when Regex.IsMatch(ipHostOrRange, RegexHelper.IPv6AddressRegex):
- bag.Add(ipHostOrRange);
- break;
-
// example.com
- case var _ when Regex.IsMatch(ipHostOrRange, RegexHelper.HostnameOrDomainRegex):
- using (var dnsResolverTask = DNSClientHelper.ResolveAorAaaaAsync(ipHostOrRange, dnsResolveHostnamePreferIPv4))
+ case var _ when Regex.IsMatch(host, RegexHelper.HostnameOrDomainRegex):
+ using (var dnsResolverTask = DNSClientHelper.ResolveAorAaaaAsync(host, dnsResolveHostnamePreferIPv4))
{
// Wait for task inside a Parallel.Foreach
- dnsResolverTask.Wait();
+ dnsResolverTask.Wait(cancellationToken);
if (!dnsResolverTask.Result.HasError)
- bag.Add($"{dnsResolverTask.Result.Value}");
+ hostsBag.Add((IPAddress.Parse($"{dnsResolverTask.Result.Value}"), host));
else
- exceptions.Enqueue(new HostNotFoundException(ipHostOrRange));
+ hostnamesNotResovledBag.Add(host);
}
break;
-
+
// example.com/24 or example.com/255.255.255.128
- case var _ when Regex.IsMatch(ipHostOrRange, RegexHelper.HostnameOrDomainWithCidrRegex) || Regex.IsMatch(ipHostOrRange, RegexHelper.HostnameOrDomainWithSubnetmaskRegex):
- var hostAndSubnet = ipHostOrRange.Split('/');
+ case var _ when Regex.IsMatch(host, RegexHelper.HostnameOrDomainWithCidrRegex):
+ case var _ when Regex.IsMatch(host, RegexHelper.HostnameOrDomainWithSubnetmaskRegex):
+ var hostAndSubnet = host.Split('/');
// Only support IPv4
using (var dnsResolverTask = DNSClientHelper.ResolveAorAaaaAsync(hostAndSubnet[0], true))
{
// Wait for task inside a Parallel.Foreach
- dnsResolverTask.Wait();
+ dnsResolverTask.Wait(cancellationToken);
if (!dnsResolverTask.Result.HasError)
{
// Only support IPv4 for ranges for now
if (dnsResolverTask.Result.Value.AddressFamily == AddressFamily.InterNetwork)
- bag.Add($"{dnsResolverTask.Result.Value}/{hostAndSubnet[1]}");
+ {
+ network = IPNetwork2.System.Net.IPNetwork.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
- exceptions.Enqueue(new HostNotFoundException(hostAndSubnet[0]));
+ {
+ hostnamesNotResovledBag.Add(hostAndSubnet[0]);
+ }
}
else
- exceptions.Enqueue(new HostNotFoundException(hostAndSubnet[0]));
+ {
+ hostnamesNotResovledBag.Add(hostAndSubnet[0]);
+ }
}
break;
}
});
- if (!exceptions.IsEmpty)
- throw new AggregateException(exceptions);
+ // Sort list and return
+ IPAddressComparer comparer = new();
- return bag.ToList();
+ return ([.. hostsBag.OrderBy(x => x.ipAddress, comparer)], [.. hostnamesNotResovledBag]);
}
-}
\ No newline at end of file
+}
diff --git a/Source/NETworkManager.Models/Network/HostnameArgs.cs b/Source/NETworkManager.Models/Network/HostnameArgs.cs
new file mode 100644
index 0000000000..5da655b21a
--- /dev/null
+++ b/Source/NETworkManager.Models/Network/HostnameArgs.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace NETworkManager.Models.Network;
+
+///
+/// Contains the information of a resolved hostname in a .
+///
+public class HostnameArgs : EventArgs
+{
+ ///
+ /// Hostname.
+ ///
+ public string Hostname { get; }
+
+ ///
+ /// Creates a new instance of with the given hostname.
+ ///
+ ///
+ public HostnameArgs(string hostname)
+ {
+ Hostname = hostname;
+ }
+}
diff --git a/Source/NETworkManager.Models/Network/IPScanner.cs b/Source/NETworkManager.Models/Network/IPScanner.cs
index 31128163b2..c41d41d860 100644
--- a/Source/NETworkManager.Models/Network/IPScanner.cs
+++ b/Source/NETworkManager.Models/Network/IPScanner.cs
@@ -24,11 +24,11 @@ public sealed class IPScanner
#region Events
- public event EventHandler HostFound;
+ public event EventHandler HostScanned;
- private void OnHostFound(IPScannerHostScannedArgs e)
+ private void OnHostScanned(IPScannerHostScannedArgs e)
{
- HostFound?.Invoke(this, e);
+ HostScanned?.Invoke(this, e);
}
public event EventHandler ScanComplete;
@@ -65,7 +65,7 @@ public IPScanner(IPScannerOptions options)
#region Methods
- public void ScanAsync(IPAddress[] ipAddresses, CancellationToken cancellationToken)
+ public void ScanAsync(IEnumerable<(IPAddress ipAddress, string hostname)> hosts, CancellationToken cancellationToken)
{
// Start the scan in a separate task
Task.Run(() =>
@@ -92,10 +92,10 @@ public void ScanAsync(IPAddress[] ipAddresses, CancellationToken cancellationTok
};
// Start scan
- Parallel.ForEach(ipAddresses, hostParallelOptions, ipAddress =>
+ Parallel.ForEach(hosts, hostParallelOptions, host =>
{
// Start ping async
- var pingTask = PingAsync(ipAddress, cancellationToken);
+ var pingTask = PingAsync(host.ipAddress, cancellationToken);
// Start port scan async
ConcurrentBag portResults = new();
@@ -105,13 +105,13 @@ public void ScanAsync(IPAddress[] ipAddresses, CancellationToken cancellationTok
Parallel.ForEach(_options.PortScanPorts, portParallelOptions, port =>
{
// Test if port is open
- using var tcpClient = new TcpClient(ipAddress.AddressFamily);
+ using var tcpClient = new TcpClient(host.ipAddress.AddressFamily);
var portState = PortState.None;
try
{
- var task = tcpClient.ConnectAsync(ipAddress, port);
+ var task = tcpClient.ConnectAsync(host.ipAddress, port);
if (task.Wait(_options.PortScanTimeout))
portState = tcpClient.Connected ? PortState.Open : PortState.Closed;
@@ -153,7 +153,7 @@ public void ScanAsync(IPAddress[] ipAddresses, CancellationToken cancellationTok
if (_options.ResolveHostname)
{
// Don't use await in Parallel.ForEach, this will break
- var dnsResolverTask = DNSClient.GetInstance().ResolvePtrAsync(ipAddress);
+ var dnsResolverTask = DNSClient.GetInstance().ResolvePtrAsync(host.ipAddress);
// Wait for task inside a Parallel.Foreach
dnsResolverTask.Wait();
@@ -174,7 +174,7 @@ public void ScanAsync(IPAddress[] ipAddresses, CancellationToken cancellationTok
{
// Get info from arp table
var arpTableInfo = ARP.GetTable()
- .FirstOrDefault(p => p.IPAddress.ToString() == ipAddress.ToString());
+ .FirstOrDefault(p => p.IPAddress.ToString() == host.ipAddress.ToString());
if (arpTableInfo != null)
macAddress = arpTableInfo.MACAddress;
@@ -183,7 +183,7 @@ public void ScanAsync(IPAddress[] ipAddresses, CancellationToken cancellationTok
if (macAddress == null)
{
var networkInterfaceInfo = networkInterfaces.FirstOrDefault(p =>
- p.IPv4Address.Any(x => x.Item1.Equals(ipAddress)));
+ p.IPv4Address.Any(x => x.Item1.Equals(host.ipAddress)));
if (networkInterfaceInfo != null)
macAddress = networkInterfaceInfo.PhysicalAddress;
@@ -199,7 +199,7 @@ public void ScanAsync(IPAddress[] ipAddresses, CancellationToken cancellationTok
}
}
- OnHostFound(new IPScannerHostScannedArgs(
+ OnHostScanned(new IPScannerHostScannedArgs(
new IPScannerHostInfo(
isReachable, pingInfo, isAnyPortOpen, portResults.OrderBy(x => x.Port).ToList(),
hostname, macAddress, vendor)));
diff --git a/Source/NETworkManager.Models/Network/Ping.cs b/Source/NETworkManager.Models/Network/Ping.cs
index df7707a451..d66b9f1355 100644
--- a/Source/NETworkManager.Models/Network/Ping.cs
+++ b/Source/NETworkManager.Models/Network/Ping.cs
@@ -16,8 +16,7 @@ public sealed class Ping
public byte[] Buffer = new byte[32];
public int TTL = 64;
public bool DontFragment = true;
- public string Hostname = string.Empty;
-
+
private const int ExceptionCancelCount = 3;
#endregion
@@ -43,6 +42,13 @@ private void OnPingException(PingExceptionArgs e)
PingException?.Invoke(this, e);
}
+ public event EventHandler HostnameResolved;
+
+ private void OnHostnameResolved(HostnameArgs e)
+ {
+ HostnameResolved?.Invoke(this, e);
+ }
+
public event EventHandler UserHasCanceled;
private void OnUserHasCanceled()
@@ -56,17 +62,18 @@ public void SendAsync(IPAddress ipAddress, CancellationToken cancellationToken)
{
Task.Run(async () =>
{
- var hostname = Hostname;
+ var hostname = string.Empty;
// Try to resolve PTR
- if (string.IsNullOrEmpty(hostname))
- {
- var dnsResult = await DNSClient.GetInstance().ResolvePtrAsync(ipAddress);
+ var dnsResult = await DNSClient.GetInstance().ResolvePtrAsync(ipAddress);
- if (!dnsResult.HasError)
- hostname = dnsResult.Value;
+ if (!dnsResult.HasError)
+ {
+ hostname = dnsResult.Value;
+
+ OnHostnameResolved(new HostnameArgs(hostname));
}
-
+
var errorCount = 0;
var options = new PingOptions
diff --git a/Source/NETworkManager.Models/Network/PingMonitorOptions.cs b/Source/NETworkManager.Models/Network/PingMonitorOptions.cs
deleted file mode 100644
index 9de14c3fb5..0000000000
--- a/Source/NETworkManager.Models/Network/PingMonitorOptions.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Net;
-
-namespace NETworkManager.Models.Network;
-
-public class PingMonitorOptions
-{
- public string Host { get; set; }
- public IPAddress IPAddress { get; set; }
-
- public PingMonitorOptions(string host, IPAddress ipAddress)
- {
- Host = host;
- IPAddress = ipAddress;
- }
-}
diff --git a/Source/NETworkManager.Models/Network/PortScanner.cs b/Source/NETworkManager.Models/Network/PortScanner.cs
index a8b6329784..6ead46e40a 100644
--- a/Source/NETworkManager.Models/Network/PortScanner.cs
+++ b/Source/NETworkManager.Models/Network/PortScanner.cs
@@ -1,6 +1,7 @@
using NETworkManager.Models.Lookup;
using NETworkManager.Utilities;
using System;
+using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
@@ -54,7 +55,7 @@ public PortScanner(PortScannerOptions options)
#endregion
#region Methods
- public void ScanAsync(IPAddress[] ipAddresses, int[] ports, CancellationToken cancellationToken)
+ public void ScanAsync(IEnumerable<(IPAddress ipAddress, string hostname)> hosts, IEnumerable ports, CancellationToken cancellationToken)
{
_progressValue = 0;
@@ -74,15 +75,15 @@ public void ScanAsync(IPAddress[] ipAddresses, int[] ports, CancellationToken ca
MaxDegreeOfParallelism = _options.MaxPortThreads
};
- Parallel.ForEach(ipAddresses, hostParallelOptions, ipAddress =>
+ Parallel.ForEach(hosts, hostParallelOptions, host =>
{
// Resolve Hostname (PTR)
var hostname = string.Empty;
if (_options.ResolveHostname)
{
- // Don't use await in Paralle.ForEach, this will break
- var dnsResolverTask = DNSClient.GetInstance().ResolvePtrAsync(ipAddress);
+ // 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);
@@ -95,13 +96,13 @@ public void ScanAsync(IPAddress[] ipAddresses, int[] ports, CancellationToken ca
Parallel.ForEach(ports, portParallelOptions, port =>
{
// Test if port is open
- using (var tcpClient = new TcpClient(ipAddress.AddressFamily))
+ using (var tcpClient = new TcpClient(host.ipAddress.AddressFamily))
{
var portState = PortState.None;
try
{
- var task = tcpClient.ConnectAsync(ipAddress, port);
+ var task = tcpClient.ConnectAsync(host.ipAddress, port);
if (task.Wait(_options.Timeout))
portState = tcpClient.Connected ? PortState.Open : PortState.Closed;
@@ -118,7 +119,7 @@ public void ScanAsync(IPAddress[] ipAddresses, int[] ports, CancellationToken ca
if (_options.ShowAllResults || portState == PortState.Open)
OnPortScanned(new PortScannerPortScannedArgs(
- new PortScannerPortInfo(ipAddress, hostname, port, PortLookup.LookupByPortAndProtocol(port), portState)));
+ new PortScannerPortInfo(host.ipAddress, hostname, port, PortLookup.LookupByPortAndProtocol(port), portState)));
}
}
diff --git a/Source/NETworkManager.Models/Network/PortScannerPortInfo.cs b/Source/NETworkManager.Models/Network/PortScannerPortInfo.cs
index d8fc4ebc64..c39385b276 100644
--- a/Source/NETworkManager.Models/Network/PortScannerPortInfo.cs
+++ b/Source/NETworkManager.Models/Network/PortScannerPortInfo.cs
@@ -23,6 +23,11 @@ public class PortScannerPortInfo : PortInfo
///
public int IPAddressInt32 => IPAddress is { AddressFamily: System.Net.Sockets.AddressFamily.InterNetwork } ? IPv4Address.ToInt32(IPAddress) : 0;
+ ///
+ /// Hostname (if available) or IP address of the host.
+ ///
+ public string HostAsString => string.IsNullOrEmpty(Hostname) ? IPAddress.ToString() : Hostname;
+
///
/// Creates a new instance of with the specified parameters.
///
diff --git a/Source/NETworkManager.Models/Network/Traceroute.cs b/Source/NETworkManager.Models/Network/Traceroute.cs
index 382bbe60b0..830247b30f 100644
--- a/Source/NETworkManager.Models/Network/Traceroute.cs
+++ b/Source/NETworkManager.Models/Network/Traceroute.cs
@@ -11,7 +11,7 @@
namespace NETworkManager.Models.Network;
-public class Traceroute
+public sealed class Traceroute
{
#region Variables
@@ -23,35 +23,35 @@ public class Traceroute
public event EventHandler HopReceived;
- protected virtual void OnHopReceived(TracerouteHopReceivedArgs e)
+ private void OnHopReceived(TracerouteHopReceivedArgs e)
{
HopReceived?.Invoke(this, e);
}
public event EventHandler TraceComplete;
- protected virtual void OnTraceComplete()
+ private void OnTraceComplete()
{
TraceComplete?.Invoke(this, EventArgs.Empty);
}
public event EventHandler MaximumHopsReached;
- protected virtual void OnMaximumHopsReached(MaximumHopsReachedArgs e)
+ private void OnMaximumHopsReached(MaximumHopsReachedArgs e)
{
MaximumHopsReached?.Invoke(this, e);
}
public event EventHandler TraceError;
- protected virtual void OnTraceError(TracerouteErrorArgs e)
+ private void OnTraceError(TracerouteErrorArgs e)
{
TraceError?.Invoke(this, e);
}
public event EventHandler UserHasCanceled;
- protected virtual void OnUserHasCanceled()
+ private void OnUserHasCanceled()
{
UserHasCanceled?.Invoke(this, EventArgs.Empty);
}
diff --git a/Source/NETworkManager.Settings/GlobalStaticConfiguration.cs b/Source/NETworkManager.Settings/GlobalStaticConfiguration.cs
index c07aed6135..37129e4094 100644
--- a/Source/NETworkManager.Settings/GlobalStaticConfiguration.cs
+++ b/Source/NETworkManager.Settings/GlobalStaticConfiguration.cs
@@ -110,6 +110,7 @@ public static class GlobalStaticConfiguration
public static int PingMonitor_Timeout => 4000;
public static int PingMonitor_TTL => 64;
public static int PingMonitor_WaitTime => 1000;
+ public static bool PingMonitor_ExpandHostView => false;
public static ExportFileType PingMonitor_ExportFileType => ExportFileType.Csv;
// Application: Traceroute
diff --git a/Source/NETworkManager.Settings/SettingsInfo.cs b/Source/NETworkManager.Settings/SettingsInfo.cs
index 99d7ea77da..690f668d29 100644
--- a/Source/NETworkManager.Settings/SettingsInfo.cs
+++ b/Source/NETworkManager.Settings/SettingsInfo.cs
@@ -1400,6 +1400,21 @@ public int PingMonitor_WaitTime
OnPropertyChanged();
}
}
+
+ private bool _pingMonitor_ExpandHostView = GlobalStaticConfiguration.PingMonitor_ExpandHostView;
+
+ public bool PingMonitor_ExpandHostView
+ {
+ get => _pingMonitor_ExpandHostView;
+ set
+ {
+ if (value == _pingMonitor_ExpandHostView)
+ return;
+
+ _pingMonitor_ExpandHostView = value;
+ OnPropertyChanged();
+ }
+ }
private string _pingMonitor_ExportFilePath;
@@ -3713,7 +3728,7 @@ public double WakeOnLAN_ProfileWidth
#endregion
- #region Whois
+ #region Whois
private ObservableCollection _whois_DomainHistory = new();
diff --git a/Source/NETworkManager.Settings/SettingsManager.cs b/Source/NETworkManager.Settings/SettingsManager.cs
index 05fc0cc665..1c31fcd1ca 100644
--- a/Source/NETworkManager.Settings/SettingsManager.cs
+++ b/Source/NETworkManager.Settings/SettingsManager.cs
@@ -180,6 +180,10 @@ public static void Upgrade(Version fromVersion, Version toVersion)
if (fromVersion < new Version(2023, 6, 27, 0))
UpgradeTo_2023_6_27_0();
+ // 2023.11.28.0
+ if (fromVersion < new Version(2023, 11, 28, 0))
+ UpgradeTo_2023_11_28_0();
+
// Latest
if (fromVersion < toVersion)
UpgradeToLatest(toVersion);
@@ -286,12 +290,11 @@ private static void UpgradeTo_2023_6_27_0()
}
///
- /// Method to apply changes for the latest version.
+ /// Method to apply changes for version 2023.11.28.0.
///
- /// Latest version.
- private static void UpgradeToLatest(Version version)
+ private static void UpgradeTo_2023_11_28_0()
{
- Log.Info($"Apply upgrade to {version}...");
+ Log.Info($"Apply upgrade to 2023.11.28.0...");
// First run is required due to the new settings
Log.Info("Set \"FirstRun\" to true...");
@@ -305,5 +308,14 @@ private static void UpgradeToLatest(Version version)
Log.Info("Init \"DNSLookup_DNSServers\" with default DNS servers...");
Current.DNSLookup_DNSServers = new ObservableCollection(DNSServer.GetDefaultList());
}
+
+ ///
+ /// Method to apply changes for the latest version.
+ ///
+ /// Latest version.
+ private static void UpgradeToLatest(Version version)
+ {
+ Log.Info($"Apply upgrade to {version}...");
+ }
#endregion
}
diff --git a/Source/NETworkManager.Utilities/DNSClient.cs b/Source/NETworkManager.Utilities/DNSClient.cs
index 6cd1c59dbc..66b69cde9d 100644
--- a/Source/NETworkManager.Utilities/DNSClient.cs
+++ b/Source/NETworkManager.Utilities/DNSClient.cs
@@ -11,7 +11,7 @@ public class DNSClient : SingletonBase
///
/// Error message which is returned when the DNS client is not configured.
///
- private static readonly string _notConfiguredMessage = "DNS client is not configured. Call Configure() first.";
+ private const string NotConfiguredMessage = "DNS client is not configured. Call Configure() first.";
///
/// Store the current DNS settings.
@@ -41,13 +41,10 @@ public void Configure(DNSClientSettings settings)
// Setup custom DNS servers
List servers = new();
- foreach (var (Server, Port) in _settings.DNSServers)
- servers.Add(new IPEndPoint(IPAddress.Parse(Server), Port));
+ foreach (var (server, port) in _settings.DNSServers)
+ servers.Add(new IPEndPoint(IPAddress.Parse(server), port));
- _client = new LookupClient(new LookupClientOptions(servers.ToArray())
- {
-
- });
+ _client = new LookupClient(new LookupClientOptions(servers.ToArray()));
}
else
{
@@ -78,7 +75,7 @@ public void UpdateFromWindows()
public async Task ResolveAAsync(string query)
{
if (!_isConfigured)
- throw new DNSClientNotConfiguredException(_notConfiguredMessage);
+ throw new DNSClientNotConfiguredException(NotConfiguredMessage);
try
{
@@ -91,10 +88,9 @@ public async Task ResolveAAsync(string query)
// Validate result because of https://github.com/BornToBeRoot/NETworkManager/issues/1934
var record = result.Answers.ARecords().FirstOrDefault();
- if (record != null)
- return new DNSClientResultIPAddress(record.Address, $"{result.NameServer}");
- else
- return new DNSClientResultIPAddress(true, $"IP address for \"{query}\" could not be resolved and the DNS server did not return an error. Try to check your DNS server with: dig @{result.NameServer.Address} {query}", $"{result.NameServer}");
+ return record != null ?
+ new DNSClientResultIPAddress(record.Address, $"{result.NameServer}") :
+ new DNSClientResultIPAddress(true, $"IP address for \"{query}\" could not be resolved and the DNS server did not return an error. Try to check your DNS server with: dig @{result.NameServer.Address} {query}", $"{result.NameServer}");
}
catch (DnsResponseException ex)
{
@@ -110,7 +106,7 @@ public async Task ResolveAAsync(string query)
public async Task ResolveAaaaAsync(string query)
{
if (!_isConfigured)
- throw new DNSClientNotConfiguredException(_notConfiguredMessage);
+ throw new DNSClientNotConfiguredException(NotConfiguredMessage);
try
{
@@ -123,10 +119,9 @@ public async Task ResolveAaaaAsync(string query)
// Validate result because of https://github.com/BornToBeRoot/NETworkManager/issues/1934
var record = result.Answers.AaaaRecords().FirstOrDefault();
- if (record != null)
- return new DNSClientResultIPAddress(record.Address, $"{result.NameServer}");
- else
- return new DNSClientResultIPAddress(true, $"IP address for \"{query}\" could not be resolved and the DNS server did not return an error. Try to check your DNS server with: dig @{result.NameServer.Address} {query}", $"{result.NameServer}");
+ return record != null ?
+ new DNSClientResultIPAddress(record.Address, $"{result.NameServer}") :
+ new DNSClientResultIPAddress(true, $"IP address for \"{query}\" could not be resolved and the DNS server did not return an error. Try to check your DNS server with: dig @{result.NameServer.Address} {query}", $"{result.NameServer}");
}
catch (DnsResponseException ex)
{
@@ -142,7 +137,7 @@ public async Task ResolveAaaaAsync(string query)
public async Task ResolveCnameAsync(string query)
{
if (!_isConfigured)
- throw new DNSClientNotConfiguredException(_notConfiguredMessage);
+ throw new DNSClientNotConfiguredException(NotConfiguredMessage);
try
{
@@ -155,10 +150,9 @@ public async Task ResolveCnameAsync(string query)
// Validate result because of https://github.com/BornToBeRoot/NETworkManager/issues/1934
var record = result.Answers.CnameRecords().FirstOrDefault();
- if (record != null)
- return new DNSClientResultString(record.CanonicalName, $"{result.NameServer}");
- else
- return new DNSClientResultString(true, $"CNAME for \"{query}\" could not be resolved and the DNS server did not return an error. Try to check your DNS server with: dig @{result.NameServer.Address} {query}", $"{result.NameServer}");
+ return record != null ?
+ new DNSClientResultString(record.CanonicalName, $"{result.NameServer}") :
+ new DNSClientResultString(true, $"CNAME for \"{query}\" could not be resolved and the DNS server did not return an error. Try to check your DNS server with: dig @{result.NameServer.Address} {query}", $"{result.NameServer}");
}
catch (DnsResponseException ex)
{
@@ -174,7 +168,7 @@ public async Task ResolveCnameAsync(string query)
public async Task ResolvePtrAsync(IPAddress ipAddress)
{
if (!_isConfigured)
- throw new DNSClientNotConfiguredException(_notConfiguredMessage);
+ throw new DNSClientNotConfiguredException(NotConfiguredMessage);
try
{
@@ -187,10 +181,9 @@ public async Task ResolvePtrAsync(IPAddress ipAddress)
// Validate result because of https://github.com/BornToBeRoot/NETworkManager/issues/1934
var record = result.Answers.PtrRecords().FirstOrDefault();
- if (record != null)
- return new DNSClientResultString(record.PtrDomainName, $"{result.NameServer}");
- else
- return new DNSClientResultString(true, $"PTR for \"{ipAddress}\" could not be resolved and the DNS server did not return an error. Try to check your DNS server with: dig @{result.NameServer.Address} -x {ipAddress}", $"{result.NameServer}");
+ return record != null ?
+ new DNSClientResultString(record.PtrDomainName, $"{result.NameServer}"):
+ new DNSClientResultString(true, $"PTR for \"{ipAddress}\" could not be resolved and the DNS server did not return an error. Try to check your DNS server with: dig @{result.NameServer.Address} -x {ipAddress}", $"{result.NameServer}");
}
catch (DnsResponseException ex)
{
diff --git a/Source/NETworkManager.Utilities/IPAddressComparer.cs b/Source/NETworkManager.Utilities/IPAddressComparer.cs
new file mode 100644
index 0000000000..410a5f06eb
--- /dev/null
+++ b/Source/NETworkManager.Utilities/IPAddressComparer.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using System.Net;
+
+namespace NETworkManager.Utilities
+{
+ public class IPAddressComparer : IComparer
+ {
+ public int Compare(IPAddress first, IPAddress second)
+ {
+ return IPAddressHelper.CompareIPAddresses(first, second);
+ }
+ }
+}
diff --git a/Source/NETworkManager.Utilities/IPAddressHelper.cs b/Source/NETworkManager.Utilities/IPAddressHelper.cs
index 7b136d9d78..2db98a4a58 100644
--- a/Source/NETworkManager.Utilities/IPAddressHelper.cs
+++ b/Source/NETworkManager.Utilities/IPAddressHelper.cs
@@ -4,6 +4,12 @@ namespace NETworkManager.Utilities;
public static class IPAddressHelper
{
+
+ ///
+ /// Test if an IP address is a private address.
+ ///
+ /// IP address.
+ /// True if the IP address is a private address, otherwise false.
public static bool IsPrivateIPAddress(IPAddress ipAddress)
{
var addressBytes = ipAddress.GetAddressBytes();
@@ -21,4 +27,22 @@ public static bool IsPrivateIPAddress(IPAddress ipAddress)
return false;
}
}
+
+ ///
+ /// Compare two IP addresses.
+ ///
+ /// First IP address.
+ /// Second IP address.
+ /// 0 if the IP addresses are equal, otherwise a negative or positive value.
+ public static int CompareIPAddresses(IPAddress first, IPAddress second)
+ {
+ byte[] bytesFirst = first.GetAddressBytes();
+ byte[] bytesSecond = second.GetAddressBytes();
+
+ for (int i = 0; i < bytesFirst.Length; i++)
+ if (bytesFirst[i] != bytesSecond[i])
+ return bytesFirst[i] - bytesSecond[i];
+
+ return 0; // IP addresses are equal
+ }
}
\ No newline at end of file
diff --git a/Source/NETworkManager/ProfileDialogManager.cs b/Source/NETworkManager/ProfileDialogManager.cs
index 29ea6988d2..3985a34043 100644
--- a/Source/NETworkManager/ProfileDialogManager.cs
+++ b/Source/NETworkManager/ProfileDialogManager.cs
@@ -21,7 +21,7 @@ public static class ProfileDialogManager
#region Dialog to add, edit, copy as and delete profile
- public static async Task ShowAddProfileDialog(IProfileManagerMinimal viewModel,
+ public static Task ShowAddProfileDialog(IProfileManagerMinimal viewModel,
IDialogCoordinator dialogCoordinator, ProfileInfo profile = null, string group = null,
ApplicationName applicationName = ApplicationName.None)
{
@@ -50,10 +50,10 @@ public static async Task ShowAddProfileDialog(IProfileManagerMinimal viewModel,
viewModel.OnProfileManagerDialogOpen();
- await dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
+ return dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
}
- public static async Task ShowEditProfileDialog(IProfileManagerMinimal viewModel,
+ public static Task ShowEditProfileDialog(IProfileManagerMinimal viewModel,
IDialogCoordinator dialogCoordinator, ProfileInfo profile)
{
CustomDialog customDialog = new()
@@ -80,10 +80,11 @@ public static async Task ShowEditProfileDialog(IProfileManagerMinimal viewModel,
};
viewModel.OnProfileManagerDialogOpen();
- await dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
+
+ return dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
}
- public static async Task ShowCopyAsProfileDialog(IProfileManagerMinimal viewModel,
+ public static Task ShowCopyAsProfileDialog(IProfileManagerMinimal viewModel,
IDialogCoordinator dialogCoordinator, ProfileInfo profile)
{
CustomDialog customDialog = new()
@@ -110,10 +111,11 @@ public static async Task ShowCopyAsProfileDialog(IProfileManagerMinimal viewMode
};
viewModel.OnProfileManagerDialogOpen();
- await dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
+
+ return dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
}
- public static async Task ShowDeleteProfileDialog(IProfileManagerMinimal viewModel,
+ public static Task ShowDeleteProfileDialog(IProfileManagerMinimal viewModel,
IDialogCoordinator dialogCoordinator, IList profiles)
{
CustomDialog customDialog = new()
@@ -144,14 +146,15 @@ public static async Task ShowDeleteProfileDialog(IProfileManagerMinimal viewMode
};
viewModel.OnProfileManagerDialogOpen();
- await dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
+
+ return dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
}
#endregion
#region Dialog to add, edit and delete group
- public static async Task ShowAddGroupDialog(IProfileManagerMinimal viewModel, IDialogCoordinator dialogCoordinator)
+ public static Task ShowAddGroupDialog(IProfileManagerMinimal viewModel, IDialogCoordinator dialogCoordinator)
{
CustomDialog customDialog = new()
{
@@ -177,10 +180,11 @@ public static async Task ShowAddGroupDialog(IProfileManagerMinimal viewModel, ID
};
viewModel.OnProfileManagerDialogOpen();
- await dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
+
+ return dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
}
- public static async Task ShowEditGroupDialog(IProfileManagerMinimal viewModel, IDialogCoordinator dialogCoordinator,
+ public static Task ShowEditGroupDialog(IProfileManagerMinimal viewModel, IDialogCoordinator dialogCoordinator,
GroupInfo group)
{
CustomDialog customDialog = new()
@@ -207,10 +211,11 @@ public static async Task ShowEditGroupDialog(IProfileManagerMinimal viewModel, I
};
viewModel.OnProfileManagerDialogOpen();
- await dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
+
+ return dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
}
- public static async Task ShowDeleteGroupDialog(IProfileManagerMinimal viewModel,
+ public static Task ShowDeleteGroupDialog(IProfileManagerMinimal viewModel,
IDialogCoordinator dialogCoordinator, GroupInfo group)
{
CustomDialog customDialog = new()
@@ -236,7 +241,8 @@ public static async Task ShowDeleteGroupDialog(IProfileManagerMinimal viewModel,
};
viewModel.OnProfileManagerDialogOpen();
- await dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
+
+ return dialogCoordinator.ShowMetroDialogAsync(viewModel, customDialog);
}
#endregion
diff --git a/Source/NETworkManager/Resources/Styles/ListBoxStyle.xaml b/Source/NETworkManager/Resources/Styles/ListBoxStyle.xaml
index d80bbe15e8..12fdcb9b33 100644
--- a/Source/NETworkManager/Resources/Styles/ListBoxStyle.xaml
+++ b/Source/NETworkManager/Resources/Styles/ListBoxStyle.xaml
@@ -12,14 +12,6 @@
\ No newline at end of file
diff --git a/Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs b/Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs
index 8d983725a2..80a8728335 100644
--- a/Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs
+++ b/Source/NETworkManager/ViewModels/DNSLookupHostViewModel.cs
@@ -165,10 +165,10 @@ public DNSLookupHostViewModel(IDialogCoordinator instance)
var tabId = Guid.NewGuid();
- TabItems = new ObservableCollection
- {
+ TabItems =
+ [
new(Localization.Resources.Strings.NewTab, new DNSLookupView (tabId), tabId)
- };
+ ];
// Profiles
SetProfilesView();
diff --git a/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs b/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs
index 82c70bf336..18a788f51b 100644
--- a/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs
+++ b/Source/NETworkManager/ViewModels/DNSLookupViewModel.cs
@@ -221,8 +221,8 @@ public DNSLookupViewModel(IDialogCoordinator instance, Guid tabId, string host)
DNSServers.SourceCollection.Cast().First();
ResultsView = CollectionViewSource.GetDefaultView(Results);
- ResultsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(DNSLookupRecordInfo.IPEndPoint)));
- ResultsView.SortDescriptions.Add(new SortDescription(nameof(DNSLookupRecordInfo.IPEndPoint),
+ ResultsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(DNSLookupRecordInfo.NameServerAsString)));
+ ResultsView.SortDescriptions.Add(new SortDescription(nameof(DNSLookupRecordInfo.NameServerIPAddress),
ListSortDirection.Descending));
LoadSettings();
@@ -406,7 +406,7 @@ private void AddHostToHistory(string host)
public void SortResultByPropertyName(string sortDescription)
{
ResultsView.SortDescriptions.Clear();
- ResultsView.SortDescriptions.Add(new SortDescription(nameof(DNSLookupRecordInfo.Server),
+ ResultsView.SortDescriptions.Add(new SortDescription(nameof(DNSLookupRecordInfo.NameServerIPAddress),
ListSortDirection.Descending));
if (_lastSortDescriptionAscending.Equals(sortDescription))
diff --git a/Source/NETworkManager/ViewModels/IPScannerViewModel.cs b/Source/NETworkManager/ViewModels/IPScannerViewModel.cs
index 19b4459041..3b455b567a 100644
--- a/Source/NETworkManager/ViewModels/IPScannerViewModel.cs
+++ b/Source/NETworkManager/ViewModels/IPScannerViewModel.cs
@@ -39,21 +39,21 @@ public class IPScannerViewModel : ViewModelBase, IProfileManagerMinimal
private readonly Guid _tabId;
private bool _firstLoad = true;
- private string _hosts;
- public string Hosts
+ private string _host;
+ public string Host
{
- get => _hosts;
+ get => _host;
set
{
- if (value == _hosts)
+ if (value == _host)
return;
- _hosts = value;
+ _host = value;
OnPropertyChanged();
}
}
- public ICollectionView HostsHistoryView { get; }
+ public ICollectionView HostHistoryView { get; }
private bool _isSubnetDetectionRunning;
public bool IsSubnetDetectionRunning
@@ -84,21 +84,21 @@ public bool IsRunning
}
}
- private bool _cancelScan;
- public bool CancelScan
+ private bool _isCanceling;
+ public bool IsCanceling
{
- get => _cancelScan;
+ get => _isCanceling;
set
{
- if (value == _cancelScan)
+ if (value == _isCanceling)
return;
- _cancelScan = value;
+ _isCanceling = value;
OnPropertyChanged();
}
}
- private ObservableCollection _results = new();
+ private ObservableCollection _results = [];
public ObservableCollection Results
{
get => _results;
@@ -220,10 +220,10 @@ public IPScannerViewModel(IDialogCoordinator instance, Guid tabId, string hostOr
_dialogCoordinator = instance;
_tabId = tabId;
- Hosts = hostOrIPRange;
+ Host = hostOrIPRange;
// Host history
- HostsHistoryView = CollectionViewSource.GetDefaultView(SettingsManager.Current.IPScanner_HostHistory);
+ HostHistoryView = CollectionViewSource.GetDefaultView(SettingsManager.Current.IPScanner_HostHistory);
// Result view
ResultsView = CollectionViewSource.GetDefaultView(Results);
@@ -237,8 +237,8 @@ public void OnLoaded()
if (!_firstLoad)
return;
- if (!string.IsNullOrEmpty(Hosts))
- StartScan().ConfigureAwait(false);
+ if (!string.IsNullOrEmpty(Host))
+ Start().ConfigureAwait(false);
_firstLoad = false;
}
@@ -260,7 +260,10 @@ private bool Scan_CanExecute(object parameter)
private void ScanAction()
{
- Scan();
+ if (IsRunning)
+ Stop();
+ else
+ Start().ConfigureAwait(false);
}
public ICommand DetectSubnetCommand => new RelayCommand(_ => DetectSubnetAction());
@@ -344,15 +347,7 @@ private void ExportAction()
#endregion
#region Methods
- private void Scan()
- {
- if (IsRunning)
- StopScan();
- else
- StartScan().ConfigureAwait(false);
- }
-
- private async Task StartScan()
+ private async Task Start()
{
IsStatusMessageDisplayed = false;
IsRunning = true;
@@ -367,51 +362,40 @@ private async Task StartScan()
{
foreach (var tabablzControl in VisualTreeHelper.FindVisualChildren(window))
{
- tabablzControl.Items.OfType().First(x => x.Id == _tabId).Header = Hosts;
+ tabablzControl.Items.OfType().First(x => x.Id == _tabId).Header = Host;
}
}
_cancellationTokenSource = new CancellationTokenSource();
// Resolve hostnames
- List ipRanges;
+ (List<(IPAddress ipAddress, string hostname)> hosts, List hostnamesNotResolved) hosts;
try
{
- ipRanges = await HostRangeHelper.ResolveHostnamesInIPRangesAsync(Hosts.Replace(" ", "").Split(';'), SettingsManager.Current.Network_ResolveHostnamePreferIPv4, _cancellationTokenSource.Token);
+ hosts = await HostRangeHelper.ResolveAsync(HostRangeHelper.CreateListFromInput(Host), SettingsManager.Current.Network_ResolveHostnamePreferIPv4, _cancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
UserHasCanceled(this, EventArgs.Empty);
return;
}
- catch (AggregateException exceptions) // DNS error (could not resolve hostname...)
- {
- DnsResolveFailed(exceptions);
- return;
- }
-
- // Create ip addresses
- IPAddress[] ipAddresses;
-
- try
+
+ // Show error message if (some) hostnames could not be resolved
+ if(hosts.hostnamesNotResolved.Count > 0)
{
- // Create a list of all ip addresses
- ipAddresses = await HostRangeHelper.CreateIPAddressesFromIPRangesAsync(ipRanges.ToArray(), _cancellationTokenSource.Token);
- }
- catch (OperationCanceledException)
- {
- UserHasCanceled(this, EventArgs.Empty);
- return;
+ StatusMessage = $"{Localization.Resources.Strings.TheFollowingHostnamesCouldNotBeResolved} {string.Join(", ", hosts.hostnamesNotResolved)}";
+ IsStatusMessageDisplayed = true;
+
}
- HostsToScan = ipAddresses.Length;
+ HostsToScan = hosts.hosts.Count;
HostsScanned = 0;
PreparingScan = false;
// Add host(s) to the history
- AddHostToHistory(Hosts);
+ AddHostToHistory(Host);
var ipScanner = new IPScanner(new IPScannerOptions(
SettingsManager.Current.IPScanner_MaxHostThreads,
@@ -428,17 +412,17 @@ await PortRangeHelper.ConvertPortRangeToIntArrayAsync(SettingsManager.Current.IP
SettingsManager.Current.IPScanner_ShowAllResults
));
- ipScanner.HostFound += HostFound;
+ ipScanner.HostScanned += HostScanned;
ipScanner.ScanComplete += ScanComplete;
ipScanner.ProgressChanged += ProgressChanged;
ipScanner.UserHasCanceled += UserHasCanceled;
- ipScanner.ScanAsync(ipAddresses, _cancellationTokenSource.Token);
+ ipScanner.ScanAsync(hosts.hosts, _cancellationTokenSource.Token);
}
- private void StopScan()
+ private void Stop()
{
- CancelScan = true;
+ IsCanceling = true;
_cancellationTokenSource.Cancel();
}
@@ -458,10 +442,10 @@ private async Task DetectIPRange()
{
subnetmaskDetected = true;
- Hosts = $"{localIP}/{Subnetmask.ConvertSubnetmaskToCidr(networkInterface.IPv4Address.First().Item2)}";
+ Host = $"{localIP}/{Subnetmask.ConvertSubnetmaskToCidr(networkInterface.IPv4Address.First().Item2)}";
// Fix: If the user clears the TextBox and then clicks again on the button, the TextBox remains empty...
- OnPropertyChanged(nameof(Hosts));
+ OnPropertyChanged(nameof(Host));
break;
}
@@ -517,13 +501,13 @@ private void AddHostToHistory(string ipRange)
// Clear the old items
SettingsManager.Current.IPScanner_HostHistory.Clear();
- OnPropertyChanged(nameof(Hosts)); // Raise property changed again, after the collection has been cleared
+ OnPropertyChanged(nameof(Host)); // Raise property changed again, after the collection has been cleared
// Fill with the new items
list.ForEach(x => SettingsManager.Current.IPScanner_HostHistory.Add(x));
}
- private async Task Export()
+ private Task Export()
{
var customDialog = new CustomDialog
{
@@ -561,20 +545,20 @@ private async Task Export()
DataContext = exportViewModel
};
- await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog);
+ return _dialogCoordinator.ShowMetroDialogAsync(this, customDialog);
}
public void OnClose()
{
// Stop scan
if (IsRunning)
- StopScan();
+ Stop();
}
#endregion
#region Events
- private void HostFound(object sender, IPScannerHostScannedArgs e)
+ private void HostScanned(object sender, IPScannerHostScannedArgs e)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate
{
@@ -582,6 +566,11 @@ private void HostFound(object sender, IPScannerHostScannedArgs e)
}));
}
+ private void ProgressChanged(object sender, ProgressChangedArgs e)
+ {
+ HostsScanned = e.Value;
+ }
+
private void ScanComplete(object sender, EventArgs e)
{
if (Results.Count == 0)
@@ -590,21 +579,7 @@ private void ScanComplete(object sender, EventArgs e)
IsStatusMessageDisplayed = true;
}
- CancelScan = false;
- IsRunning = false;
- }
-
- private void ProgressChanged(object sender, ProgressChangedArgs e)
- {
- HostsScanned = e.Value;
- }
-
- private void DnsResolveFailed(AggregateException e)
- {
- StatusMessage = $"{Localization.Resources.Strings.TheFollowingHostnamesCouldNotBeResolved} {string.Join(", ", e.Flatten().InnerExceptions.Select(x => x.Message))}";
- IsStatusMessageDisplayed = true;
-
- CancelScan = false;
+ IsCanceling = false;
IsRunning = false;
}
@@ -612,6 +587,9 @@ private void UserHasCanceled(object sender, EventArgs e)
{
StatusMessage = Localization.Resources.Strings.CanceledByUserMessage;
IsStatusMessageDisplayed = true;
+
+ IsCanceling = false;
+ IsRunning = false;
}
#endregion
}
diff --git a/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs b/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs
index 6fae457237..85b9d1d9bb 100644
--- a/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs
+++ b/Source/NETworkManager/ViewModels/PingMonitorHostViewModel.cs
@@ -10,11 +10,14 @@
using System.Linq;
using System.Collections.ObjectModel;
using NETworkManager.Views;
-using System.Net;
using NETworkManager.Profiles;
using System.Windows.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
+using System.Diagnostics;
+using System.Net;
+using System.Threading;
+using MahApps.Metro.Controls;
using NETworkManager.Models;
namespace NETworkManager.ViewModels;
@@ -23,6 +26,9 @@ public class PingMonitorHostViewModel : ViewModelBase, IProfileManager
{
#region Variables
private readonly IDialogCoordinator _dialogCoordinator;
+
+ private CancellationTokenSource _cancellationTokenSource;
+
private readonly DispatcherTimer _searchDispatcherTimer = new();
private readonly bool _isLoading;
@@ -57,6 +63,20 @@ public bool IsRunning
OnPropertyChanged();
}
}
+
+ private bool _isCanceling;
+ public bool IsCanceling
+ {
+ get => _isCanceling;
+ set
+ {
+ if (value == _isCanceling)
+ return;
+
+ _isCanceling = value;
+ OnPropertyChanged();
+ }
+ }
private bool _isStatusMessageDisplayed;
public bool IsStatusMessageDisplayed
@@ -86,7 +106,7 @@ private set
}
}
- private ObservableCollection _hosts = new();
+ private ObservableCollection _hosts = [];
public ObservableCollection Hosts
{
get => _hosts;
@@ -229,7 +249,7 @@ public GridLength ProfileWidth
public PingMonitorHostViewModel(IDialogCoordinator instance)
{
_isLoading = true;
-
+
_dialogCoordinator = instance;
// Host history
@@ -262,35 +282,48 @@ private void LoadSettings()
#endregion
#region ICommands & Actions
- public ICommand AddHostCommand => new RelayCommand(_ => AddHostAction());
+ public ICommand PingCommand => new RelayCommand(_ => PingAction(), Ping_CanExecute);
- private async void AddHostAction()
+ private bool Ping_CanExecute(object parameter)
{
- await AddHost(Host).ConfigureAwait(true);
-
- AddHostToHistory(Host);
- Host = string.Empty;
+ return Application.Current.MainWindow != null && !((MetroWindow)Application.Current.MainWindow).IsAnyDialogOpen;
}
-
- public ICommand ExportCommand => new RelayCommand(_ => ExportAction());
-
- private void ExportAction()
+
+ private void PingAction()
{
- SelectedHost?.Export();
+ if (IsRunning)
+ Stop();
+ else
+ Start().ConfigureAwait(false);
}
+
+ public ICommand PingProfileCommand => new RelayCommand(_ => PingProfileAction(), PingProfile_CanExecute);
- public ICommand AddHostProfileCommand => new RelayCommand(_ => AddHostProfileAction(), AddHostProfile_CanExecute);
-
- private bool AddHostProfile_CanExecute(object obj)
+ private bool PingProfile_CanExecute(object obj)
{
return !IsSearching && SelectedProfile != null;
}
- private void AddHostProfileAction()
+ private void PingProfileAction()
{
- AddHost(SelectedProfile.PingMonitor_Host).ConfigureAwait(false);
+ if(SetHost(SelectedProfile.PingMonitor_Host))
+ Start().ConfigureAwait(false);
}
+
+ public ICommand CloseAllCommand => new RelayCommand(_ => CloseAllAction());
+
+ private void CloseAllAction()
+ {
+ RemoveAllHosts();
+ }
+
+ public ICommand ExportCommand => new RelayCommand(_ => ExportAction());
+ private void ExportAction()
+ {
+ SelectedHost?.Export();
+ }
+
public ICommand AddProfileCommand => new RelayCommand(_ => AddProfileAction());
private void AddProfileAction()
@@ -337,77 +370,114 @@ private void ClearSearchAction()
#endregion
#region Methods
- public async Task AddHost(string hosts)
+
+ ///
+ /// Set the host to ping.
+ ///
+ /// Host to ping
+ /// True if the host was set successfully, otherwise false
+ public bool SetHost(string host)
{
- IsStatusMessageDisplayed = false;
- StatusMessage = string.Empty;
+ // Check if it is already running or canceling
+ if (IsRunning || IsCanceling)
+ {
+ _dialogCoordinator.ShowMessageAsync(this, Localization.Resources.Strings.Error, Localization.Resources.Strings.CannotSetHostWhileRunningMessage);
+
+ return false;
+ }
+
+ Host = host;
+ return true;
+ }
+
+ public async Task Start()
+ {
+ IsStatusMessageDisplayed = false;
IsRunning = true;
+
+ _cancellationTokenSource = new CancellationTokenSource();
+
+ // Resolve hostnames
+ (List<(IPAddress ipAddress, string hostname)> hosts, List hostnamesNotResolved) hosts;
- await Task.Run(() =>
+ try
{
- Parallel.ForEach(hosts.Split(';'), currentHost =>
+ hosts = await HostRangeHelper.ResolveAsync(HostRangeHelper.CreateListFromInput(Host), SettingsManager.Current.Network_ResolveHostnamePreferIPv4, _cancellationTokenSource.Token);
+ }
+ catch (OperationCanceledException)
+ {
+ UserHasCanceled();
+
+ return;
+ }
+
+ // Show error message if (some) hostnames could not be resolved
+ if(hosts.hostnamesNotResolved.Count > 0)
+ {
+ StatusMessage = $"{Localization.Resources.Strings.TheFollowingHostnamesCouldNotBeResolved} {string.Join(", ", hosts.hostnamesNotResolved)}";
+ IsStatusMessageDisplayed = true;
+ }
+
+ // Add host(s) to history
+ AddHostToHistory(Host);
+
+ // Add host(s) to list and start the ping
+ foreach (var hostView in hosts.hosts.Select(currentHost => new PingMonitorView(Guid.NewGuid(), RemoveHostByGuid, currentHost)))
+ {
+ // Check if the user has canceled the operation
+ if (_cancellationTokenSource.IsCancellationRequested)
{
- var host = currentHost.Trim();
- var hostname = string.Empty;
-
- // Resolve ip address from hostname
- if (!IPAddress.TryParse(host, out var ipAddress))
- {
- hostname = host;
-
- using var dnsResolverTask = DNSClientHelper.ResolveAorAaaaAsync(host, SettingsManager.Current.Network_ResolveHostnamePreferIPv4);
-
- // Wait for task inside a Parallel.Foreach
- dnsResolverTask.Wait();
-
- if (dnsResolverTask.Result.HasError)
- {
- StatusMessageShowOrAdd(host, dnsResolverTask.Result);
- return;
- }
-
- ipAddress = dnsResolverTask.Result.Value;
- }
-
- // Resolve hostname from ip address
- else
- {
- using var dnsResolverTask = DNSClient.GetInstance().ResolvePtrAsync(ipAddress);
-
- // Wait for task inside a Parallel.Foreach
- dnsResolverTask.Wait();
-
- // Hostname is not necessary for ping. Don't show an error message in the UI.
- if (!dnsResolverTask.Result.HasError)
- hostname = dnsResolverTask.Result.Value;
- }
+ UserHasCanceled();
+
+ return;
+ }
+
+ Hosts.Add(hostView);
+
+ // Start the ping
+ hostView.Start();
+
+ // Wait a bit to prevent the UI from freezing
+ await Task.Delay(25);
+ }
+
+ Host = string.Empty;
+
+ IsCanceling = false;
+ IsRunning = false;
+ }
+
+ private void Stop()
+ {
+ IsCanceling = true;
+ _cancellationTokenSource.Cancel();
+ }
- Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate
- {
- Hosts.Add(new PingMonitorView(Guid.NewGuid(), RemoveHost, new PingMonitorOptions(hostname, ipAddress)));
- }));
- });
-
- IsRunning = false;
- }).ConfigureAwait(true);
+ private void RemoveAllHosts()
+ {
+ for (var i = Hosts.Count - 1; i >= 0; i--)
+ {
+ Hosts[i].Stop();
+ Hosts.RemoveAt(i);
+ }
}
- private void RemoveHost(Guid hostId)
+ private void RemoveHostByGuid(Guid hostId)
{
- var index = -1;
+ var i = -1;
foreach (var host in Hosts)
{
if (host.HostId.Equals(hostId))
- index = Hosts.IndexOf(host);
+ i = Hosts.IndexOf(host);
}
- if (index == -1)
+ if (i == -1)
return;
-
- Hosts[index].CloseView();
- Hosts.RemoveAt(index);
+
+ Hosts[i].Stop();
+ Hosts.RemoveAt(i);
}
private void AddHostToHistory(string host)
@@ -422,7 +492,7 @@ private void AddHostToHistory(string host)
// Fill with the new items
list.ForEach(x => SettingsManager.Current.PingMonitor_HostHistory.Add(x));
}
-
+
private void ResizeProfile(bool dueToChangedSize)
{
_canProfileWidthChange = false;
@@ -502,29 +572,6 @@ private void RefreshProfiles()
SetProfilesView(SelectedProfile);
}
-
- ///
- /// Method to display the status message and append messages related to .
- ///
- /// Host which should be resolved.
- /// Information about the error that occurred in the query.
- private void StatusMessageShowOrAdd(string host, DNSClientResult result)
- {
- Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate
- {
- // Show the message
- if (!IsStatusMessageDisplayed)
- {
- StatusMessage = DNSClientHelper.FormatDNSClientResultError(host, result);
- IsStatusMessageDisplayed = true;
-
- return;
- }
-
- // Append the message
- StatusMessage += Environment.NewLine + DNSClientHelper.FormatDNSClientResultError(host, result);
- }));
- }
#endregion
#region Event
@@ -541,5 +588,14 @@ private void SearchDispatcherTimer_Tick(object sender, EventArgs e)
IsSearching = false;
}
+
+ private void UserHasCanceled()
+ {
+ StatusMessage = Localization.Resources.Strings.CanceledByUserMessage;
+ IsStatusMessageDisplayed = true;
+
+ IsCanceling = false;
+ IsRunning = false;
+ }
#endregion
}
diff --git a/Source/NETworkManager/ViewModels/PingMonitorSettingsViewModel.cs b/Source/NETworkManager/ViewModels/PingMonitorSettingsViewModel.cs
index 0c8ae45f9e..d0d4551cde 100644
--- a/Source/NETworkManager/ViewModels/PingMonitorSettingsViewModel.cs
+++ b/Source/NETworkManager/ViewModels/PingMonitorSettingsViewModel.cs
@@ -91,6 +91,23 @@ public int WaitTime
OnPropertyChanged();
}
}
+
+ private bool _expandHostView;
+ public bool ExpandHostView
+ {
+ get => _expandHostView;
+ set
+ {
+ if (value == _expandHostView)
+ return;
+
+ if (!_isLoading)
+ SettingsManager.Current.PingMonitor_ExpandHostView = value;
+
+ _expandHostView = value;
+ OnPropertyChanged();
+ }
+ }
#endregion
#region Contructor, load settings
@@ -110,6 +127,7 @@ private void LoadSettings()
TTL = SettingsManager.Current.PingMonitor_TTL;
DontFragment = SettingsManager.Current.PingMonitor_DontFragment;
WaitTime = SettingsManager.Current.PingMonitor_WaitTime;
+ ExpandHostView = SettingsManager.Current.PingMonitor_ExpandHostView;
}
#endregion
}
diff --git a/Source/NETworkManager/ViewModels/PingMonitorViewModel.cs b/Source/NETworkManager/ViewModels/PingMonitorViewModel.cs
index 478a7ee5b7..5fc62ddc6a 100644
--- a/Source/NETworkManager/ViewModels/PingMonitorViewModel.cs
+++ b/Source/NETworkManager/ViewModels/PingMonitorViewModel.cs
@@ -26,21 +26,34 @@ public class PingMonitorViewModel : ViewModelBase
private CancellationTokenSource _cancellationTokenSource;
public readonly Guid HostId;
- private readonly Action _closeCallback;
- private bool _firstLoad = true;
-
+ private readonly Action _removeHostByGuid;
+
private List _pingInfoList;
- private readonly string _host;
- public string Host
+ private string _title;
+ public string Title
{
- get => _host;
- private init
+ get => _title;
+ private set
{
- if (value == _host)
+ if (value == _title)
return;
- _host = value;
+ _title = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private string _hostname;
+ public string Hostname
+ {
+ get => _hostname;
+ private set
+ {
+ if (value == _hostname)
+ return;
+
+ _hostname = value;
OnPropertyChanged();
}
}
@@ -143,6 +156,34 @@ public int Lost
}
}
+ private double _packetLoss;
+ public double PacketLoss
+ {
+ get => _packetLoss;
+ set
+ {
+ if (Math.Abs(value - _packetLoss) < 0.01)
+ return;
+
+ _packetLoss = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private long _timeMs;
+ public long TimeMs
+ {
+ get => _timeMs;
+ set
+ {
+ if (value == _timeMs)
+ return;
+
+ _timeMs = value;
+ OnPropertyChanged();
+ }
+ }
+
private void InitialTimeChart()
{
var dayConfig = Mappers.Xy()
@@ -194,31 +235,39 @@ public bool IsErrorMessageDisplayed
OnPropertyChanged();
}
}
+
+ private bool _expandHostView;
+ public bool ExpandHostView
+ {
+ get => _expandHostView;
+ set
+ {
+ if (value == _expandHostView)
+ return;
+
+ _expandHostView = value;
+ OnPropertyChanged();
+ }
+ }
#endregion
#region Contructor, load settings
- public PingMonitorViewModel(IDialogCoordinator instance, Guid hostId, Action closeCallback, PingMonitorOptions options)
+ public PingMonitorViewModel(IDialogCoordinator instance, Guid hostId, Action removeHostByGuid, (IPAddress ipAddress, string hostname) host)
{
_dialogCoordinator = instance;
HostId = hostId;
- _closeCallback = closeCallback;
-
- Host = options.Host;
- IPAddress = options.IPAddress;
-
- InitialTimeChart();
- }
+ _removeHostByGuid = removeHostByGuid;
- public void OnLoaded()
- {
- if (!_firstLoad)
- return;
+ Title = string.IsNullOrEmpty(host.hostname) ? host.ipAddress.ToString() : $"{host.hostname} # {host.ipAddress}";
- StartPing();
+ IPAddress = host.ipAddress;
+ Hostname = host.hostname;
- _firstLoad = false;
- }
+ InitialTimeChart();
+
+ ExpandHostView = SettingsManager.Current.PingMonitor_ExpandHostView;
+ }
#endregion
#region ICommands & Actions
@@ -226,39 +275,35 @@ public void OnLoaded()
private void PingAction()
{
- Ping();
+ if (IsRunning)
+ Stop();
+ else
+ Start();
}
public ICommand CloseCommand => new RelayCommand(_ => CloseAction());
private void CloseAction()
{
- _closeCallback(HostId);
+ _removeHostByGuid(HostId);
}
#endregion
#region Methods
- private void Ping()
- {
- if (IsRunning)
- StopPing();
- else
- StartPing();
- }
-
- private void StartPing()
+ public void Start()
{
IsErrorMessageDisplayed = false;
IsRunning = true;
// Reset history
- _pingInfoList = new List();
+ _pingInfoList = [];
// Reset the latest results
StatusTime = DateTime.Now;
Transmitted = 0;
Received = 0;
Lost = 0;
+ PacketLoss = 0;
// Reset chart
ResetTimeChart();
@@ -271,19 +316,22 @@ private void StartPing()
Buffer = new byte[SettingsManager.Current.PingMonitor_Buffer],
TTL = SettingsManager.Current.PingMonitor_TTL,
DontFragment = SettingsManager.Current.PingMonitor_DontFragment,
- WaitTime = SettingsManager.Current.PingMonitor_WaitTime,
- Hostname = Host
+ WaitTime = SettingsManager.Current.PingMonitor_WaitTime
};
ping.PingReceived += Ping_PingReceived;
ping.PingException += Ping_PingException;
+ ping.HostnameResolved += Ping_HostnameResolved;
ping.UserHasCanceled += Ping_UserHasCanceled;
ping.SendAsync(IPAddress, _cancellationTokenSource.Token);
}
-
- private void StopPing()
+
+ public void Stop()
{
+ if (!IsRunning)
+ return;
+
_cancellationTokenSource?.Cancel();
}
@@ -330,10 +378,9 @@ public async Task Export()
SettingsManager.Current.PingMonitor_ExportFileType = instance.FileType;
SettingsManager.Current.PingMonitor_ExportFilePath = instance.FilePath;
}, _ => { _dialogCoordinator.HideMetroDialogAsync(this, customDialog); },
- new[]
- {
+ [
ExportFileType.Csv, ExportFileType.Xml, ExportFileType.Json
- }, false,
+ ], false,
SettingsManager.Current.PingMonitor_ExportFileType,
SettingsManager.Current.PingMonitor_ExportFilePath);
@@ -344,13 +391,6 @@ public async Task Export()
await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog);
}
-
- public void OnClose()
- {
- // Stop the ping
- if (IsRunning)
- StopPing();
- }
#endregion
#region Events
@@ -385,8 +425,12 @@ private void Ping_PingReceived(object sender, PingReceivedArgs e)
timeInfo = new LvlChartsDefaultInfo(e.Args.Timestamp, double.NaN);
}
+
+ PacketLoss = Math.Round((double)Lost / Transmitted * 100, 2);
+ TimeMs = e.Args.Time;
- Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate
+ // Null exception may occur when the application is closing
+ Application.Current?.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate
{
Series[0].Values.Add(timeInfo);
@@ -403,6 +447,15 @@ private void Ping_UserHasCanceled(object sender, EventArgs e)
IsRunning = false;
}
+ private void Ping_HostnameResolved(object sender, HostnameArgs e)
+ {
+ // Update title if name was not set in the constructor
+ if (string.IsNullOrEmpty(Hostname))
+ Title = $"{e.Hostname.TrimEnd('.')} # {IPAddress}";
+
+ Hostname = e.Hostname;
+ }
+
private void Ping_PingException(object sender, PingExceptionArgs e)
{
IsRunning = false;
diff --git a/Source/NETworkManager/ViewModels/PortScannerViewModel.cs b/Source/NETworkManager/ViewModels/PortScannerViewModel.cs
index e0cc570acd..8278749c28 100644
--- a/Source/NETworkManager/ViewModels/PortScannerViewModel.cs
+++ b/Source/NETworkManager/ViewModels/PortScannerViewModel.cs
@@ -35,21 +35,21 @@ public class PortScannerViewModel : ViewModelBase
private string _lastSortDescriptionAscending = string.Empty;
- private string _hosts;
- public string Hosts
+ private string _host;
+ public string Host
{
- get => _hosts;
+ get => _host;
set
{
- if (value == _hosts)
+ if (value == _host)
return;
- _hosts = value;
+ _host = value;
OnPropertyChanged();
}
}
- public ICollectionView HostsHistoryView { get; }
+ public ICollectionView HostHistoryView { get; }
private string _ports;
public string Ports
@@ -82,21 +82,21 @@ public bool IsRunning
}
}
- private bool _cancelScan;
- public bool CancelScan
+ private bool _isCanceling;
+ public bool IsCanceling
{
- get => _cancelScan;
+ get => _isCanceling;
set
{
- if (value == _cancelScan)
+ if (value == _isCanceling)
return;
- _cancelScan = value;
+ _isCanceling = value;
OnPropertyChanged();
}
}
- private ObservableCollection _results = new();
+ private ObservableCollection _results = [];
public ObservableCollection Results
{
get => _results;
@@ -216,16 +216,16 @@ public PortScannerViewModel(IDialogCoordinator instance, Guid tabId, string host
_dialogCoordinator = instance;
_tabId = tabId;
- Hosts = host;
+ Host = host;
Ports = port;
// Set collection view
- HostsHistoryView = CollectionViewSource.GetDefaultView(SettingsManager.Current.PortScanner_HostHistory);
+ HostHistoryView = CollectionViewSource.GetDefaultView(SettingsManager.Current.PortScanner_HostHistory);
PortsHistoryView = CollectionViewSource.GetDefaultView(SettingsManager.Current.PortScanner_PortHistory);
// Result view
ResultsView = CollectionViewSource.GetDefaultView(Results);
- ResultsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(PortScannerPortInfo.IPAddress)));
+ ResultsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(PortScannerPortInfo.HostAsString)));
ResultsView.SortDescriptions.Add(new SortDescription(nameof(PortScannerPortInfo.IPAddressInt32), ListSortDirection.Descending));
LoadSettings();
@@ -241,8 +241,8 @@ public void OnLoaded()
if (!_firstLoad)
return;
- if (!string.IsNullOrEmpty(Hosts) && !string.IsNullOrEmpty(Ports))
- StartScan().ConfigureAwait(false);
+ if (!string.IsNullOrEmpty(Host) && !string.IsNullOrEmpty(Ports))
+ Start().ConfigureAwait(false);
_firstLoad = false;
}
@@ -251,7 +251,7 @@ public void OnClose()
{
// Stop scan
if (IsRunning)
- StopScan();
+ Stop();
}
#endregion
@@ -272,9 +272,9 @@ private void OpenPortProfileSelectionAction()
private void ScanAction()
{
if (IsRunning)
- StopScan();
+ Stop();
else
- StartScan().ConfigureAwait(false);
+ Start().ConfigureAwait(false);
}
public ICommand ExportCommand => new RelayCommand(_ => ExportAction());
@@ -311,7 +311,7 @@ private async Task OpenPortProfileSelection()
await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog);
}
- private async Task StartScan()
+ private async Task Start()
{
IsStatusMessageDisplayed = false;
StatusMessage = string.Empty;
@@ -328,53 +328,43 @@ private async Task StartScan()
{
foreach (var tabablzControl in VisualTreeHelper.FindVisualChildren(window))
{
- tabablzControl.Items.OfType().First(x => x.Id == _tabId).Header = Hosts;
+ tabablzControl.Items.OfType().First(x => x.Id == _tabId).Header = Host;
}
}
_cancellationTokenSource = new CancellationTokenSource();
// Resolve hostnames
- List ipRanges;
+ (List<(IPAddress ipAddress, string hostname)> hosts, List hostnamesNotResolved) hosts;
try
{
- ipRanges = await HostRangeHelper.ResolveHostnamesInIPRangesAsync(Hosts.Replace(" ", "").Split(';'), SettingsManager.Current.Network_ResolveHostnamePreferIPv4, _cancellationTokenSource.Token);
+ hosts = await HostRangeHelper.ResolveAsync(HostRangeHelper.CreateListFromInput(Host), SettingsManager.Current.Network_ResolveHostnamePreferIPv4, _cancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
UserHasCanceled(this, EventArgs.Empty);
return;
}
- catch (AggregateException exceptions) // DNS error (could not resolve hostname...)
- {
- DnsResolveFailed(exceptions);
- return;
- }
-
- // Create ip addresses
- IPAddress[] ipAddresses;
-
- try
- {
- // Create a list of all ip addresses
- ipAddresses = await HostRangeHelper.CreateIPAddressesFromIPRangesAsync(ipRanges.ToArray(), _cancellationTokenSource.Token);
- }
- catch (OperationCanceledException)
+
+ // Show error message if (some) hostnames could not be resolved
+ if(hosts.hostnamesNotResolved.Count > 0)
{
- UserHasCanceled(this, EventArgs.Empty);
- return;
+ StatusMessage = $"{Localization.Resources.Strings.TheFollowingHostnamesCouldNotBeResolved} {string.Join(", ", hosts.hostnamesNotResolved)}";
+ IsStatusMessageDisplayed = true;
+
}
+ // Convert ports to int array
var ports = await PortRangeHelper.ConvertPortRangeToIntArrayAsync(Ports);
- PortsToScan = ports.Length * ipAddresses.Length;
+ PortsToScan = ports.Length * hosts.hosts.Count;
PortsScanned = 0;
PreparingScan = false;
// Add host(s) to the history
- AddHostToHistory(Hosts);
+ AddHostToHistory(Host);
AddPortToHistory(Ports);
var portScanner = new PortScanner(new PortScannerOptions(
@@ -390,12 +380,12 @@ private async Task StartScan()
portScanner.ProgressChanged += ProgressChanged;
portScanner.UserHasCanceled += UserHasCanceled;
- portScanner.ScanAsync(ipAddresses, ports, _cancellationTokenSource.Token);
+ portScanner.ScanAsync(hosts.hosts, ports, _cancellationTokenSource.Token);
}
- private void StopScan()
+ private void Stop()
{
- CancelScan = true;
+ IsCanceling = true;
_cancellationTokenSource.Cancel();
}
@@ -441,7 +431,7 @@ private void AddHostToHistory(string host)
// Clear the old items
SettingsManager.Current.PortScanner_HostHistory.Clear();
- OnPropertyChanged(nameof(Hosts)); // Raise property changed again, after the collection has been cleared
+ OnPropertyChanged(nameof(Host)); // Raise property changed again, after the collection has been cleared
// Fill with the new items
list.ForEach(x => SettingsManager.Current.PortScanner_HostHistory.Add(x));
@@ -479,13 +469,12 @@ public void SortResultByPropertyName(string sortDescription)
#endregion
#region Events
- private void UserHasCanceled(object sender, EventArgs e)
+ private void PortScanned(object sender, PortScannerPortScannedArgs e)
{
- StatusMessage = Localization.Resources.Strings.CanceledByUserMessage;
- IsStatusMessageDisplayed = true;
-
- CancelScan = false;
- IsRunning = false;
+ Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate
+ {
+ Results.Add(e.Args);
+ }));
}
private void ProgressChanged(object sender, ProgressChangedArgs e)
@@ -493,15 +482,6 @@ private void ProgressChanged(object sender, ProgressChangedArgs e)
PortsScanned = e.Value;
}
- private void DnsResolveFailed(AggregateException e)
- {
- StatusMessage = $"{Localization.Resources.Strings.TheFollowingHostnamesCouldNotBeResolved} {string.Join(", ", e.Flatten().InnerExceptions.Select(x => x.Message))}";
- IsStatusMessageDisplayed = true;
-
- CancelScan = false;
- IsRunning = false;
- }
-
private void ScanComplete(object sender, EventArgs e)
{
if (Results.Count == 0)
@@ -510,16 +490,17 @@ private void ScanComplete(object sender, EventArgs e)
IsStatusMessageDisplayed = true;
}
- CancelScan = false;
+ IsCanceling = false;
IsRunning = false;
}
-
- private void PortScanned(object sender, PortScannerPortScannedArgs e)
+
+ private void UserHasCanceled(object sender, EventArgs e)
{
- Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate
- {
- Results.Add(e.Args);
- }));
+ StatusMessage = Localization.Resources.Strings.CanceledByUserMessage;
+ IsStatusMessageDisplayed = true;
+
+ IsCanceling = false;
+ IsRunning = false;
}
#endregion
diff --git a/Source/NETworkManager/Views/DiscoveryProtocolView.xaml b/Source/NETworkManager/Views/DiscoveryProtocolView.xaml
index 035982fc23..03a219e8b5 100644
--- a/Source/NETworkManager/Views/DiscoveryProtocolView.xaml
+++ b/Source/NETworkManager/Views/DiscoveryProtocolView.xaml
@@ -81,9 +81,18 @@
-
-
-
+
+
+
-
-
+
-
+
@@ -595,6 +600,9 @@
diff --git a/Source/NETworkManager/Views/IPScannerView.xaml.cs b/Source/NETworkManager/Views/IPScannerView.xaml.cs
index af4c347d77..314dc9c5db 100644
--- a/Source/NETworkManager/Views/IPScannerView.xaml.cs
+++ b/Source/NETworkManager/Views/IPScannerView.xaml.cs
@@ -27,7 +27,7 @@ private void UserControl_Loaded(object sender, RoutedEventArgs e)
_viewModel.OnLoaded();
}
- private void Dispatcher_ShutdownStarted(object sender, System.EventArgs e)
+ private void Dispatcher_ShutdownStarted(object sender, EventArgs e)
{
_viewModel.OnClose();
}
diff --git a/Source/NETworkManager/Views/PingMonitorHostView.xaml b/Source/NETworkManager/Views/PingMonitorHostView.xaml
index 3ba5ac2621..ab2f82751b 100644
--- a/Source/NETworkManager/Views/PingMonitorHostView.xaml
+++ b/Source/NETworkManager/Views/PingMonitorHostView.xaml
@@ -15,10 +15,11 @@
dialogs:DialogParticipation.Register="{Binding}"
mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:PingMonitorHostViewModel}">
-
-
+
+
+
@@ -53,25 +54,29 @@
-
-
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -155,7 +217,16 @@
-
+
@@ -214,7 +285,7 @@
-
+
-