-> PortScanner added

This commit is contained in:
Kevin Krüger
2022-01-18 09:14:22 +01:00
parent f7b63cb8e1
commit a07655faed
18 changed files with 1301 additions and 9 deletions

View File

@@ -0,0 +1,12 @@
using Networking.Pages;
using System.Threading;
using System.Windows.Forms;
namespace PortScanner
{
internal interface IScannerManagerSingleton
{
void ExecuteOnceAsync(string hostname, int port, int timeout, ScannerManagerSingleton.ScanMode scanMode, WF_PortScanner.ExecuteOnceAsyncCallback callback, CancellationToken ct);
void ExecuteRangeAsync(string hostname, int portMin, int portMax, int timeout, ScannerManagerSingleton.ScanMode scanMode, ProgressBar progress, WF_PortScanner.ExecuteOnceAsyncCallback callback, CancellationToken ct);
}
}

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace PortScanner
{
static class InputChecker
{
// Check that a hostname string is valid
public static bool IsValidHostname(string hostname)
{
return hostname != "";
}
// Check that a port is valid - returns -1 if port is invalid
public static int ParsePort(string portString)
{
int port;
try
{
port = Int32.Parse(portString);
}
// If any exception occurs, the string was not a proper port
catch (Exception)
{
return -1;
}
if (port < 1 || port > 65535)
{
return -1;
}
return port;
}
// Check that timeout combobox user input is valid...
// Accepted formats: [time] ms, [time]ms, [time]
public static int ParseTimeout(string timeoutString)
{
// The regex that is used for matching the input against
var regex = new Regex(@"^\d*\s*(ms)?$");
// Try matching the user input
if (!regex.IsMatch(timeoutString))
{
return -1;
}
// Slice off the "ms" part of the string
timeoutString = Regex.Match(timeoutString, @"\d+").Value;
int timeout = Int32.Parse(timeoutString);
// Doesn't work too well with a very short timeout period
if (timeout < 250 || timeout > 20000)
{
return -1;
}
return timeout;
}
}
}

View File

@@ -0,0 +1,28 @@
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
// This is the base class for all Port Scanners
namespace PortScanner
{
abstract class PortScannerBase
{
// Hostname and port properties for scanning
public string Hostname { get; set; }
public int Port { get; set; }
// Timeout property that specifies how long to wait for an answer
public int Timeout { get; set; }
// Base construcor - just set up default values for properties
public PortScannerBase()
{
Hostname = "127.0.0.1";
Port = 22;
Timeout = 1000;
}
// Check that the hostname is listening on the port - asynchronously
public abstract Task<bool> CheckOpenAsync(CancellationToken ct);
}
}

View File

@@ -0,0 +1,111 @@
using Networking.Pages;
using System;
using System.Threading;
using System.Windows.Forms;
namespace PortScanner
{
public class ScannerManagerSingleton : IScannerManagerSingleton
{
// The instance variable - this is a singleton class
private static ScannerManagerSingleton _instance;
// The PortScanner used to scan ports
private PortScannerBase portScanner;
// Enumeration for scanning modes
public enum ScanMode
{
TCP = 1,
UDP = 2
}
// Private constructor - this is a singleton class
private ScannerManagerSingleton()
{
}
// Instance property - this is a singleton class
public static ScannerManagerSingleton Instance
{
get
{
if (_instance == null)
_instance = new ScannerManagerSingleton();
return _instance;
}
}
// Instantiate the correct type of PortScanner
private void InstantiatePortScanner(ScanMode scanMode)
{
switch (scanMode)
{
case ScanMode.TCP:
portScanner = new TCPPortScanner();
break;
case ScanMode.UDP:
portScanner = new UDPPortScanner();
break;
}
}
// Scan one port asynchronously
public async void ExecuteOnceAsync(string hostname, int port, int timeout, ScanMode scanMode, WF_PortScanner.ExecuteOnceAsyncCallback callback, CancellationToken ct)
{
// Instantiate a PortScanner
InstantiatePortScanner(scanMode);
// Assign values
portScanner.Hostname = hostname;
portScanner.Port = port;
portScanner.Timeout = timeout;
// Await for the result of this operation
var task = portScanner.CheckOpenAsync(ct);
await task;
// If a cancellation request has been triggered through CancellationToken ct, we must advise the callback function
bool cancelled = ct.IsCancellationRequested;
// Callback with the result and the port
callback(port, task.Result, cancelled, true);
}
// Scan a range of ports asynchronously
public async void ExecuteRangeAsync(string hostname, int portMin, int portMax, int timeout, ScanMode scanMode, ProgressBar progress, WF_PortScanner.ExecuteOnceAsyncCallback callback, CancellationToken ct)
{
// Instantiate a PortScanner
InstantiatePortScanner(scanMode);
// Assign first values
portScanner.Hostname = hostname;
portScanner.Timeout = timeout;
bool isLast = false;
bool cancelled = false;
for (int i = portMin; i <= portMax && !cancelled; i++)
{
if (i == portMax)
{
isLast = true;
}
portScanner.Port = i;
int percent = 100 * i / portMax;
progress.Value = percent;
var task = portScanner.CheckOpenAsync(ct);
await task;
cancelled = ct.IsCancellationRequested;
callback(i, task.Result, cancelled, isLast);
}
}
}
}

View File

@@ -0,0 +1,28 @@
namespace PortScanner.Properties {
// This class allows you to handle specific events on the settings class:
// The SettingChanging event is raised before a setting's value is changed.
// The PropertyChanged event is raised after a setting's value is changed.
// The SettingsLoaded event is raised after the setting values are loaded.
// The SettingsSaving event is raised before the setting values are saved.
internal sealed partial class Settings {
public Settings() {
// // To add event handlers for saving and changing settings, uncomment the lines below:
//
// this.SettingChanging += this.SettingChangingEventHandler;
//
// this.SettingsSaving += this.SettingsSavingEventHandler;
//
}
private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) {
// Add code to handle the SettingChangingEvent event here.
}
private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) {
// Add code to handle the SettingsSaving event here.
}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace PortScanner
{
class TCPPortScanner : PortScannerBase
{
// The TCP client for port scanning
private TcpClient tcpClient;
// Constructor - uses base class constructor
public TCPPortScanner() : base()
{
}
// Implementing the base's abstract method CheckOpenAsync(), cancellation token ct passed from MainWindow, triggered in the cancel button click event
public override async Task<bool> CheckOpenAsync(CancellationToken ct)
{
using (tcpClient = new TcpClient())
{
// connection is the Task returned by ConnectAsync
var connection = tcpClient.ConnectAsync(Hostname, Port);
bool returnValue;
// In case the ct is triggered, this will act as if delay expired right when the click occurrs
if (await Task.WhenAny(connection, Task.Delay(Timeout, ct)) == connection)
{
// If connection was refused, return false
// The exception within the task is a SocketException if the connection failed
if (connection.Exception != null)
{
returnValue = false;
}
else
{
returnValue = true;
}
}
else
{
// Timeout occurred, this means that there is no connection and port is closed
returnValue = false;
}
tcpClient.Close();
return returnValue;
}
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// This class represents one item in the list of items that will be
// displayed in the timeout time combo box in the MainWindow
namespace PortScanner
{
class TimeoutListItem
{
// DisplayMember: the string that will be displayed in the timeout combo box
// ValueMember: the actual ms value attached to that string
public string DisplayMember { get; set; }
public int ValueMember { get; set; }
// The array of different values present in the combo box
// Add new values right here ......
private static int[] _times =
{
500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000
};
// Returns a list of objects of this class intended to be used
// as a datasource for a combo box
public static List<TimeoutListItem> CreateTimeoutListItems()
{
var returnList = new List<TimeoutListItem>();
for (int i = 0; i < _times.Length; i++)
{
returnList.Add(new TimeoutListItem
{
DisplayMember = String.Format("{0} ms", _times[i]),
ValueMember = _times[i]
});
}
return returnList;
}
}
}

View File

@@ -0,0 +1,94 @@
using Networking.Pages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PortScanner
{
class UDPPortScanner : PortScannerBase
{
// The UDP client used for scanning a port
private UdpClient udpClient;
// Constructor - use base constructor
public UDPPortScanner() : base()
{
}
// TODO:
public async override Task<bool> CheckOpenAsync(CancellationToken ct)
{
// We are using a UDP client to see whether the port is open or not
// Therefore, the absence of a response means that the port is open
// If there is any respone, it is closed
using (udpClient = new UdpClient())
{
bool returnVal;
try
{
// Connect to the server
udpClient.Connect(Hostname, Port);
// Set the timeout
udpClient.Client.ReceiveTimeout = Timeout;
// Sends a message over UDP
Byte[] sendBytes = Encoding.ASCII.GetBytes("Are you open?");
udpClient.Send(sendBytes, sendBytes.Length);
// IPEndPoint object will allow us to read datagrams sent from any source.
// Port 0 means any available port
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
// Asynchronously begin receiving
var result = udpClient.ReceiveAsync();
if (await Task.WhenAny(result, Task.Delay(Timeout, ct)) == result)
{
Console.WriteLine(Encoding.ASCII.GetString(result.Result.Buffer));
returnVal = false;
}
else
{
// There was no response, we will consider this port as open
returnVal = true;
}
udpClient.Close();
return returnVal;
}
catch (SocketException e)
{
Console.WriteLine("Error Code: " + e.ErrorCode);
switch (e.ErrorCode)
{
case 10054:
returnVal = false;
break;
case 11001:
returnVal = false;
// Display an error message on the main thread
MessageBox.Show(
"Hostname could not be resolved.",
"Connection Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
break;
default:
returnVal = true;
break;
}
}
udpClient.Close();
return returnVal;
}
}
}
}