diff --git a/Networking/Networking.csproj b/Networking/Networking.csproj index 8739f68..732b4cf 100644 --- a/Networking/Networking.csproj +++ b/Networking/Networking.csproj @@ -40,8 +40,13 @@ ..\packages\PTConverter.Plugin.1.0.3\lib\net472\PTConverter.Plugin.dll + + + + + @@ -63,15 +68,23 @@ + + PortScanner.xaml + + + UserControl + + + WF_PortScanner.cs + + + IPScanner.xaml IPv4.xaml - - PortScanner.xaml - UserControl @@ -79,7 +92,10 @@ WF_IPScanner.cs + + + @@ -105,6 +121,11 @@ + + + + + @@ -130,6 +151,9 @@ WF_IPScanner.cs + + WF_PortScanner.cs + \ No newline at end of file diff --git a/Networking/Pages/PortScanner.xaml b/Networking/Pages/PortScanner.xaml index c466d71..a1348ac 100644 --- a/Networking/Pages/PortScanner.xaml +++ b/Networking/Pages/PortScanner.xaml @@ -7,6 +7,8 @@ mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" Background="White"> - - + + + + diff --git a/Networking/Pages/PortScanner.xaml.cs b/Networking/Pages/PortScanner.xaml.cs index 5672db3..c66edc1 100644 --- a/Networking/Pages/PortScanner.xaml.cs +++ b/Networking/Pages/PortScanner.xaml.cs @@ -17,7 +17,7 @@ using System.Windows.Shapes; namespace Networking.Pages { /// - /// Interaktionslogik für Networking.xaml + /// Interaktionslogik für PortScanner.xaml /// public partial class PortScanner : UserControl, IPage { @@ -30,6 +30,6 @@ namespace Networking.Pages public UserControl GetPage() => new PortScanner(); - public string GetUnderCategory() => "PortScan"; + public string GetUnderCategory() => "PortScanner"; } } diff --git a/Networking/Pages/WF_PortScanner.Designer.cs b/Networking/Pages/WF_PortScanner.Designer.cs new file mode 100644 index 0000000..d645595 --- /dev/null +++ b/Networking/Pages/WF_PortScanner.Designer.cs @@ -0,0 +1,281 @@ +namespace Networking.Pages +{ + partial class WF_PortScanner + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Komponenten-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.hostnameTextBox = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.modeGroupBox = new System.Windows.Forms.GroupBox(); + this.udpModeRadioButton = new System.Windows.Forms.RadioButton(); + this.tcpModeRadioButton = new System.Windows.Forms.RadioButton(); + this.portRangeCheckBox = new System.Windows.Forms.CheckBox(); + this.portTextBoxMax = new System.Windows.Forms.TextBox(); + this.dashLabel = new System.Windows.Forms.Label(); + this.portTextBoxMin = new System.Windows.Forms.TextBox(); + this.portLabel = new System.Windows.Forms.Label(); + this.timeoutGroupBox = new System.Windows.Forms.GroupBox(); + this.timeoutComboBox = new System.Windows.Forms.ComboBox(); + this.checkPortButton = new System.Windows.Forms.Button(); + this.listview1 = new System.Windows.Forms.ListView(); + this.chPort = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chStatus = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.progressBar1 = new System.Windows.Forms.ProgressBar(); + this.cancelButton = new System.Windows.Forms.Button(); + this.modeGroupBox.SuspendLayout(); + this.timeoutGroupBox.SuspendLayout(); + this.SuspendLayout(); + // + // hostnameTextBox + // + this.hostnameTextBox.Location = new System.Drawing.Point(38, 24); + this.hostnameTextBox.Name = "hostnameTextBox"; + this.hostnameTextBox.Size = new System.Drawing.Size(251, 20); + this.hostnameTextBox.TabIndex = 1; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(5, 27); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(29, 13); + this.label3.TabIndex = 5; + this.label3.Text = "Host"; + // + // modeGroupBox + // + this.modeGroupBox.Controls.Add(this.udpModeRadioButton); + this.modeGroupBox.Controls.Add(this.tcpModeRadioButton); + this.modeGroupBox.Location = new System.Drawing.Point(296, 19); + this.modeGroupBox.Name = "modeGroupBox"; + this.modeGroupBox.Size = new System.Drawing.Size(111, 57); + this.modeGroupBox.TabIndex = 13; + this.modeGroupBox.TabStop = false; + this.modeGroupBox.Text = "Mode"; + // + // udpModeRadioButton + // + this.udpModeRadioButton.AutoSize = true; + this.udpModeRadioButton.Location = new System.Drawing.Point(13, 35); + this.udpModeRadioButton.Name = "udpModeRadioButton"; + this.udpModeRadioButton.Size = new System.Drawing.Size(48, 17); + this.udpModeRadioButton.TabIndex = 11; + this.udpModeRadioButton.Text = "UDP"; + this.udpModeRadioButton.UseVisualStyleBackColor = true; + // + // tcpModeRadioButton + // + this.tcpModeRadioButton.AutoSize = true; + this.tcpModeRadioButton.Checked = true; + this.tcpModeRadioButton.Location = new System.Drawing.Point(13, 16); + this.tcpModeRadioButton.Name = "tcpModeRadioButton"; + this.tcpModeRadioButton.Size = new System.Drawing.Size(46, 17); + this.tcpModeRadioButton.TabIndex = 10; + this.tcpModeRadioButton.TabStop = true; + this.tcpModeRadioButton.Text = "TCP"; + this.tcpModeRadioButton.UseVisualStyleBackColor = true; + // + // portRangeCheckBox + // + this.portRangeCheckBox.AutoSize = true; + this.portRangeCheckBox.Location = new System.Drawing.Point(156, 57); + this.portRangeCheckBox.Name = "portRangeCheckBox"; + this.portRangeCheckBox.Size = new System.Drawing.Size(80, 17); + this.portRangeCheckBox.TabIndex = 19; + this.portRangeCheckBox.Text = "Port Range"; + this.portRangeCheckBox.UseVisualStyleBackColor = true; + this.portRangeCheckBox.CheckedChanged += new System.EventHandler(this.portRangeCheckBox_CheckedChanged); + // + // portTextBoxMax + // + this.portTextBoxMax.Enabled = false; + this.portTextBoxMax.Location = new System.Drawing.Point(98, 55); + this.portTextBoxMax.MaxLength = 5; + this.portTextBoxMax.Name = "portTextBoxMax"; + this.portTextBoxMax.Size = new System.Drawing.Size(38, 20); + this.portTextBoxMax.TabIndex = 18; + // + // dashLabel + // + this.dashLabel.AutoSize = true; + this.dashLabel.Location = new System.Drawing.Point(82, 58); + this.dashLabel.Name = "dashLabel"; + this.dashLabel.Size = new System.Drawing.Size(10, 13); + this.dashLabel.TabIndex = 17; + this.dashLabel.Text = "-"; + // + // portTextBoxMin + // + this.portTextBoxMin.Location = new System.Drawing.Point(38, 55); + this.portTextBoxMin.MaxLength = 5; + this.portTextBoxMin.Name = "portTextBoxMin"; + this.portTextBoxMin.Size = new System.Drawing.Size(38, 20); + this.portTextBoxMin.TabIndex = 16; + // + // portLabel + // + this.portLabel.AutoSize = true; + this.portLabel.Location = new System.Drawing.Point(3, 58); + this.portLabel.Name = "portLabel"; + this.portLabel.Size = new System.Drawing.Size(31, 13); + this.portLabel.TabIndex = 15; + this.portLabel.Text = "Ports"; + // + // timeoutGroupBox + // + this.timeoutGroupBox.Controls.Add(this.timeoutComboBox); + this.timeoutGroupBox.Location = new System.Drawing.Point(413, 24); + this.timeoutGroupBox.Name = "timeoutGroupBox"; + this.timeoutGroupBox.Size = new System.Drawing.Size(110, 52); + this.timeoutGroupBox.TabIndex = 20; + this.timeoutGroupBox.TabStop = false; + this.timeoutGroupBox.Text = "Timeout"; + // + // timeoutComboBox + // + this.timeoutComboBox.AccessibleRole = System.Windows.Forms.AccessibleRole.None; + this.timeoutComboBox.FormattingEnabled = true; + this.timeoutComboBox.Items.AddRange(new object[] { + "500 ms", + "1000 ms", + "1500 ms", + "2000 ms"}); + this.timeoutComboBox.Location = new System.Drawing.Point(6, 21); + this.timeoutComboBox.Name = "timeoutComboBox"; + this.timeoutComboBox.Size = new System.Drawing.Size(98, 21); + this.timeoutComboBox.TabIndex = 0; + // + // checkPortButton + // + this.checkPortButton.Location = new System.Drawing.Point(529, 21); + this.checkPortButton.Name = "checkPortButton"; + this.checkPortButton.Size = new System.Drawing.Size(105, 31); + this.checkPortButton.TabIndex = 5; + this.checkPortButton.Text = "Scan"; + this.checkPortButton.UseVisualStyleBackColor = true; + this.checkPortButton.Click += new System.EventHandler(this.checkPortButton_Click); + // + // listview1 + // + this.listview1.Activation = System.Windows.Forms.ItemActivation.OneClick; + this.listview1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listview1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chPort, + this.chStatus}); + this.listview1.FullRowSelect = true; + this.listview1.GridLines = true; + this.listview1.HideSelection = false; + this.listview1.HoverSelection = true; + this.listview1.Location = new System.Drawing.Point(0, 89); + this.listview1.MultiSelect = false; + this.listview1.Name = "listview1"; + this.listview1.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.listview1.Size = new System.Drawing.Size(800, 361); + this.listview1.TabIndex = 21; + this.listview1.UseCompatibleStateImageBehavior = false; + this.listview1.View = System.Windows.Forms.View.Details; + // + // chPort + // + this.chPort.Text = "Port"; + this.chPort.Width = 191; + // + // chStatus + // + this.chStatus.Text = "Status"; + this.chStatus.Width = 296; + // + // progressBar1 + // + this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.progressBar1.Location = new System.Drawing.Point(0, 79); + this.progressBar1.Name = "progressBar1"; + this.progressBar1.Size = new System.Drawing.Size(800, 10); + this.progressBar1.TabIndex = 22; + // + // cancelButton + // + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(529, 52); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(105, 23); + this.cancelButton.TabIndex = 23; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); + // + // WF_PortScanner + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.progressBar1); + this.Controls.Add(this.listview1); + this.Controls.Add(this.checkPortButton); + this.Controls.Add(this.timeoutGroupBox); + this.Controls.Add(this.portRangeCheckBox); + this.Controls.Add(this.portTextBoxMax); + this.Controls.Add(this.dashLabel); + this.Controls.Add(this.portTextBoxMin); + this.Controls.Add(this.portLabel); + this.Controls.Add(this.modeGroupBox); + this.Controls.Add(this.label3); + this.Controls.Add(this.hostnameTextBox); + this.Name = "WF_PortScanner"; + this.Size = new System.Drawing.Size(800, 450); + this.modeGroupBox.ResumeLayout(false); + this.modeGroupBox.PerformLayout(); + this.timeoutGroupBox.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.TextBox hostnameTextBox; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.GroupBox modeGroupBox; + private System.Windows.Forms.RadioButton udpModeRadioButton; + private System.Windows.Forms.RadioButton tcpModeRadioButton; + private System.Windows.Forms.CheckBox portRangeCheckBox; + private System.Windows.Forms.TextBox portTextBoxMax; + private System.Windows.Forms.Label dashLabel; + private System.Windows.Forms.TextBox portTextBoxMin; + private System.Windows.Forms.Label portLabel; + private System.Windows.Forms.GroupBox timeoutGroupBox; + private System.Windows.Forms.ComboBox timeoutComboBox; + private System.Windows.Forms.Button checkPortButton; + private System.Windows.Forms.ListView listview1; + private System.Windows.Forms.ColumnHeader chPort; + private System.Windows.Forms.ColumnHeader chStatus; + private System.Windows.Forms.ProgressBar progressBar1; + private System.Windows.Forms.Button cancelButton; + } +} diff --git a/Networking/Pages/WF_PortScanner.cs b/Networking/Pages/WF_PortScanner.cs new file mode 100644 index 0000000..4cc8bf8 --- /dev/null +++ b/Networking/Pages/WF_PortScanner.cs @@ -0,0 +1,271 @@ +using PortScanner; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Networking.Pages +{ + public partial class WF_PortScanner : UserControl + { + // Delegate to report back with one open port + public delegate void ExecuteOnceCallback(int openPort); + + // Delegate to report back with one open port (Async) + public delegate void ExecuteOnceAsyncCallback(int port, bool isOpen, bool isCancelled, bool isLast); + + // The manager instance + IScannerManagerSingleton smc; + + // Cancellation token source for the cancel button + private CancellationTokenSource cts; + + // Current mode of operation + private ScannerManagerSingleton.ScanMode currentScanMode; + + public WF_PortScanner() + { + InitializeComponent(); + this.Load += WF_PortScanner_Load; + } + + private void WF_PortScanner_Load(object sender, EventArgs e) + { + // Get the ScannerManagerSingleton instance + smc = ScannerManagerSingleton.Instance; + + // Populate the timeout times list box + PopulateTimeoutListBox(); + } + + private void PopulateTimeoutListBox() + { + // Assign the list to the ComboBox's DataSource property + timeoutComboBox.DataSource = TimeoutListItem.CreateTimeoutListItems(); + timeoutComboBox.DisplayMember = "DisplayMember"; + timeoutComboBox.ValueMember = "ValueMember"; + + // Set default value + timeoutComboBox.SelectedValue = 2000; + } + + // This method is used as a callback for portscanning - writes to the log box (text box) + public void PortResult(int port, bool isOpen, bool isCancelled, bool isLast) + { + string status; + + // The operation has been cancelled by MainWindow + if (isCancelled) + { + status = "Operation cancelled." + Environment.NewLine; + } + + // The port is open + else if (isOpen) + { + //status = String.Format("{0}, {1} port {2} is open.{3}", hostnameTextBox.Text, currentScanMode.ToString(), port, Environment.NewLine); + var item = new ListViewItem(new[] { port.ToString(), "Open" }); + listview1.Items.Add(item); + } + + // The port is closed + else + { + status = String.Format("{0}, {1} port {2} is closed.{3}", hostnameTextBox.Text, currentScanMode.ToString(), port, Environment.NewLine); + + } + + + if (isLast || isCancelled) + ToggleInputs(true); + } + + // Checks whether the timeout combo box has user input or not + private bool IsTimeoutComboBoxUserInput() + { + var inputText = timeoutComboBox.Text; + + foreach (var displayMemberText in (List)timeoutComboBox.DataSource) + { + if (displayMemberText.DisplayMember == inputText) + { + // Select the one that's in the box's list to prevent some problems + // This will return because the user input IS in the combo box's DataSource + // However it is still user input and does not have a ValueMember. Exception + // will be thrown. + timeoutComboBox.SelectedItem = displayMemberText; + return false; + } + } + + return true; + } + + // Executed when the Check Port button is clicked + private void checkPortButton_Click(object sender, EventArgs e) + { + listview1.Items.Clear(); + + // Get user inputs + string hostname = hostnameTextBox.Text; + if (hostname == "") + { + MessageBox.Show("Please enter a valid hostname.", + "Input Error", + MessageBoxButtons.OK, + MessageBoxIcon.Error); + hostnameTextBox.Focus(); + return; + } + + // Check port + int portMin = InputChecker.ParsePort(portTextBoxMin.Text); + if (portMin == -1) + { + MessageBox.Show((portRangeCheckBox.Checked ? "Lower limit of port range" : "Port") + " invalid.", + "Input Error", + MessageBoxButtons.OK, + MessageBoxIcon.Error); + portTextBoxMin.Focus(); + return; + } + + // Get scan mode + ScannerManagerSingleton.ScanMode scanMode = ReadScanMode(); + + // If custom timeout time, verify correct user input + int timeout; + if (IsTimeoutComboBoxUserInput()) + { + // If valid, proceed with that input as timeout + timeout = InputChecker.ParseTimeout(timeoutComboBox.Text); + if (timeout == -1) + { + MessageBox.Show("Timeout format: [time], [time]ms or [time] ms.\nTimeout must be between 250 ms and 20000 ms.", + "Timeout Error", + MessageBoxButtons.OK, + MessageBoxIcon.Error); + return; + } + } + else + { + // Else, use the ValueMember of the selected Member + timeout = (int)timeoutComboBox.SelectedValue; + } + + // Instantiate CTS + cts = new CancellationTokenSource(); + + // Simple one port check + if (!portRangeCheckBox.Checked) + { + // The callback for scan result + var callback = new ExecuteOnceAsyncCallback(PortResult); + + // Send one check request and toggle user inputs + ToggleInputs(false); + smc.ExecuteOnceAsync(hostname, portMin, timeout, scanMode, callback, cts.Token); + } + + // Port range check + else + { + // Verify input + int portMax = InputChecker.ParsePort(portTextBoxMax.Text); + if (portMax == -1) + { + MessageBox.Show("Upper limit of port range invalid.", + "Input Error", + MessageBoxButtons.OK, + MessageBoxIcon.Error); + portTextBoxMax.Focus(); + return; + } + + if (portMax < portMin) + { + MessageBox.Show("Port range invalid.", + "Input Error", + MessageBoxButtons.OK, + MessageBoxIcon.Error); + portTextBoxMax.Focus(); + return; + } + + // The callback for scan result + var callback = new ExecuteOnceAsyncCallback(PortResult); + + // Set status box text + //var connectionText = String.Format("Connecting to {0}, port {1}...{2}", hostname, portMin, + // Environment.NewLine); + //statusTextBox.AppendText(connectionText); + //Logger.Info(connectionText); + // Toggle inputs and begin operation + ToggleInputs(false); + smc.ExecuteRangeAsync(hostname, portMin, portMax, timeout, scanMode,progressBar1, callback, cts.Token); + } + } + // Read scan mode radio button selection + private ScannerManagerSingleton.ScanMode ReadScanMode() + { + if (tcpModeRadioButton.Checked) + { + currentScanMode = ScannerManagerSingleton.ScanMode.TCP; + } + else + { + currentScanMode = ScannerManagerSingleton.ScanMode.UDP; + } + return currentScanMode; + } + + private void portRangeCheckBox_CheckedChanged(object sender, EventArgs e) + { + // This enables or disables the max. port input box + if (portRangeCheckBox.Checked) + { + portTextBoxMax.Enabled = true; + } + else + { + portTextBoxMax.Enabled = false; + } + } + + // Toggle all inputs + private void ToggleInputs(bool setting) + { + hostnameTextBox.Enabled = setting; + portTextBoxMin.Enabled = setting; + checkPortButton.Enabled = setting; + portTextBoxMax.Enabled = setting; + portRangeCheckBox.Enabled = setting; + + // Re-disable the portMax text box + if (!portRangeCheckBox.Checked) + { + portTextBoxMax.Enabled = false; + } + + // Set focus to hostnameTextBox + if (setting) + hostnameTextBox.Focus(); + } + private void cancelButton_Click(object sender, EventArgs e) + { + // If cts is instantiated (i.e. the scanning operation is in progress, request cancellation + if (cts != null) + { + cts.Cancel(); + progressBar1.Value = 0; + } + } + } +} diff --git a/Networking/Pages/WF_PortScanner.resx b/Networking/Pages/WF_PortScanner.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Networking/Pages/WF_PortScanner.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Networking/PluginInfo.cs b/Networking/PluginInfo.cs index e70ed7f..2e00660 100644 --- a/Networking/PluginInfo.cs +++ b/Networking/PluginInfo.cs @@ -25,7 +25,7 @@ namespace Networking public IEnumerable RegisterPages => new List() { {new IPv4() }, - {new PortScanner() }, + {new Pages.PortScanner() }, {new Pages.IPScanner() } }; } diff --git a/Networking/PortScanner/IScannerManagerSingleton.cs b/Networking/PortScanner/IScannerManagerSingleton.cs new file mode 100644 index 0000000..e6a1032 --- /dev/null +++ b/Networking/PortScanner/IScannerManagerSingleton.cs @@ -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); + } +} \ No newline at end of file diff --git a/Networking/PortScanner/InputChecker.cs b/Networking/PortScanner/InputChecker.cs new file mode 100644 index 0000000..1a276ee --- /dev/null +++ b/Networking/PortScanner/InputChecker.cs @@ -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; + } + } +} diff --git a/Networking/PortScanner/PortScannerBase.cs b/Networking/PortScanner/PortScannerBase.cs new file mode 100644 index 0000000..16fbc64 --- /dev/null +++ b/Networking/PortScanner/PortScannerBase.cs @@ -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 CheckOpenAsync(CancellationToken ct); + } +} \ No newline at end of file diff --git a/Networking/PortScanner/ScannerManagerSingleton.cs b/Networking/PortScanner/ScannerManagerSingleton.cs new file mode 100644 index 0000000..7d8428f --- /dev/null +++ b/Networking/PortScanner/ScannerManagerSingleton.cs @@ -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); + } + } + } +} + diff --git a/Networking/PortScanner/Settings.cs b/Networking/PortScanner/Settings.cs new file mode 100644 index 0000000..2fac4b8 --- /dev/null +++ b/Networking/PortScanner/Settings.cs @@ -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. + } + } +} diff --git a/Networking/PortScanner/TCPPortScanner.cs b/Networking/PortScanner/TCPPortScanner.cs new file mode 100644 index 0000000..a7c9658 --- /dev/null +++ b/Networking/PortScanner/TCPPortScanner.cs @@ -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 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; + } + } + } +} \ No newline at end of file diff --git a/Networking/PortScanner/TimeoutListItem.cs b/Networking/PortScanner/TimeoutListItem.cs new file mode 100644 index 0000000..1468d17 --- /dev/null +++ b/Networking/PortScanner/TimeoutListItem.cs @@ -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 CreateTimeoutListItems() + { + var returnList = new List(); + + for (int i = 0; i < _times.Length; i++) + { + returnList.Add(new TimeoutListItem + { + DisplayMember = String.Format("{0} ms", _times[i]), + ValueMember = _times[i] + }); + } + + return returnList; + } + } +} diff --git a/Networking/PortScanner/UDPPortScanner.cs b/Networking/PortScanner/UDPPortScanner.cs new file mode 100644 index 0000000..9ab8378 --- /dev/null +++ b/Networking/PortScanner/UDPPortScanner.cs @@ -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 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; + } + } + } +} diff --git a/Networking/Util/HelperMethods.cs b/Networking/Util/HelperMethods.cs new file mode 100644 index 0000000..a096dc3 --- /dev/null +++ b/Networking/Util/HelperMethods.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PortScanner.Util +{ + public static class HelperMethods + { + public static void PauseForXSeconds(int secondsPaused) + { + secondsPaused = (int)TimeSpanUtil.ConvertMillisecondsToSeconds(secondsPaused); + System.Threading.Thread.Sleep(secondsPaused); + } + + public static void PauseForXMinutes(int minutesPaused) + { + minutesPaused = (int)TimeSpanUtil.ConvertMinutesToMilliseconds(minutesPaused); + System.Threading.Thread.Sleep(minutesPaused); + } + } +} diff --git a/Networking/Util/TimeSpanUtil.cs b/Networking/Util/TimeSpanUtil.cs new file mode 100644 index 0000000..930874d --- /dev/null +++ b/Networking/Util/TimeSpanUtil.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PortScanner.Util +{ + public static class TimeSpanUtil + { + #region To days + + public static double ConvertMillisecondsToDays(double milliseconds) + { + return TimeSpan.FromMilliseconds(milliseconds).TotalDays; + } + + public static double ConvertSecondsToDays(double seconds) + { + return TimeSpan.FromSeconds(seconds).TotalDays; + } + + public static double ConvertMinutesToDays(double minutes) + { + return TimeSpan.FromMinutes(minutes).TotalDays; + } + + public static double ConvertHoursToDays(double hours) + { + return TimeSpan.FromHours(hours).TotalDays; + } + + #endregion To days + + #region To hours + + public static double ConvertMillisecondsToHours(double milliseconds) + { + return TimeSpan.FromMilliseconds(milliseconds).TotalHours; + } + + public static double ConvertSecondsToHours(double seconds) + { + return TimeSpan.FromSeconds(seconds).TotalHours; + } + + public static double ConvertMinutesToHours(double minutes) + { + return TimeSpan.FromMinutes(minutes).TotalHours; + } + + public static double ConvertDaysToHours(double days) + { + return TimeSpan.FromHours(days).TotalHours; + } + + #endregion To hours + + #region To minutes + + public static double ConvertMillisecondsToMinutes(double milliseconds) + { + return TimeSpan.FromMilliseconds(milliseconds).TotalMinutes; + } + + public static double ConvertSecondsToMinutes(double seconds) + { + return TimeSpan.FromSeconds(seconds).TotalMinutes; + } + + public static double ConvertHoursToMinutes(double hours) + { + return TimeSpan.FromHours(hours).TotalMinutes; + } + + public static double ConvertDaysToMinutes(double days) + { + return TimeSpan.FromDays(days).TotalMinutes; + } + + #endregion To minutes + + #region To seconds + + public static double ConvertMillisecondsToSeconds(double milliseconds) + { + return TimeSpan.FromMilliseconds(milliseconds).TotalSeconds; + } + + public static double ConvertMinutesToSeconds(double minutes) + { + return TimeSpan.FromMinutes(minutes).TotalSeconds; + } + + public static double ConvertHoursToSeconds(double hours) + { + return TimeSpan.FromHours(hours).TotalSeconds; + } + + public static double ConvertDaysToSeconds(double days) + { + return TimeSpan.FromDays(days).TotalSeconds; + } + + #endregion To seconds + + #region To milliseconds + + public static double ConvertSecondsToMilliseconds(double seconds) + { + return TimeSpan.FromSeconds(seconds).TotalMilliseconds; + } + + public static double ConvertMinutesToMilliseconds(double minutes) + { + return TimeSpan.FromMinutes(minutes).TotalMilliseconds; + } + + public static double ConvertHoursToMilliseconds(double hours) + { + return TimeSpan.FromHours(hours).TotalMilliseconds; + } + + public static double ConvertDaysToMilliseconds(double days) + { + return TimeSpan.FromDays(days).TotalMilliseconds; + } + + #endregion To milliseconds + } +} diff --git a/WpfApp1/MainWindow.xaml b/WpfApp1/MainWindow.xaml index 46cd2c2..38796c7 100644 --- a/WpfApp1/MainWindow.xaml +++ b/WpfApp1/MainWindow.xaml @@ -9,7 +9,7 @@ Title="MainWindow" Height="450" Width="800"> - +