diff --git a/ColorControl/ColorControl.csproj b/ColorControl/ColorControl.csproj index 9f482d1..5264086 100644 --- a/ColorControl/ColorControl.csproj +++ b/ColorControl/ColorControl.csproj @@ -27,7 +27,7 @@ ColorControl Maassoft 0 - 1.5.3.0 + 1.5.4.0 false true true diff --git a/ColorControl/Properties/AssemblyInfo.cs b/ColorControl/Properties/AssemblyInfo.cs index fe2ca0f..2a69284 100644 --- a/ColorControl/Properties/AssemblyInfo.cs +++ b/ColorControl/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.5.3.0")] -[assembly: AssemblyFileVersion("1.5.3.0")] +[assembly: AssemblyVersion("1.5.4.0")] +[assembly: AssemblyFileVersion("1.5.4.0")] diff --git a/ColorControl/WOL.cs b/ColorControl/WOL.cs index 97ae727..97234be 100644 --- a/ColorControl/WOL.cs +++ b/ColorControl/WOL.cs @@ -1,12 +1,10 @@ using NLog; using System; -using System.Globalization; using PacketDotNet; using System.Net.NetworkInformation; using SharpPcap; -using System.Diagnostics; using SharpPcap.Npcap; -using System.Linq; +using System.Net; namespace ColorControl { @@ -14,8 +12,12 @@ class WOL { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - //You need SharpPcap for this to work + public static void WakeFunction2(string macAddress) + { + //PhysicalAddress.Parse(macAddress).SendWol(); + } + //You need SharpPcap for this to work public static void WakeFunction(string macAddress) { /* Retrieve the device list */ @@ -40,7 +42,14 @@ public static void WakeFunction(string macAddress) // } //} - Logger.Debug("Opening network device: " + device.Name); + if (device is NpcapDevice npcapDevice) + { + Logger.Debug($"Opening network device (NpcapDevice): FriendlyName: {npcapDevice.Interface.FriendlyName}, Description: {npcapDevice.Interface.Description}"); + } + else + { + Logger.Debug($"Opening network device (NOT A NpcapDevice!): {device.Name}, Description: {device.Description}"); + } //Open the device device.Open(); diff --git a/WakeOnLan/ArpRequest.cs b/WakeOnLan/ArpRequest.cs new file mode 100644 index 0000000..7a1aa59 --- /dev/null +++ b/WakeOnLan/ArpRequest.cs @@ -0,0 +1,39 @@ +using System.ComponentModel; +using System.Net.NetworkInformation; +using System.Threading.Tasks; + +namespace System.Net +{ + /// Stellt Methoden für das Senden von Anfragen über das ARP-Protokoll bereit. + public static class ArpRequest + { + /// + /// Sendet eine Anfrage über das ARP-Protokoll, um eine IP-Adresse in die Physikalische Adresse aufzulösen. Falls sich die physikalische Adresse bereits im Cache des Hosts befindet, wird diese zurückgegeben. + /// + /// Destination . + /// Eine ArpRequestResult-Instanz, welche die Ergebnisse der Anfrage enthält. + public static ArpRequestResult Send(IPAddress destination) + { + if (destination == null) + throw new ArgumentNullException(nameof(destination)); + + int destIp = BitConverter.ToInt32(destination.GetAddressBytes(), 0); + + var addr = new byte[6]; + var len = addr.Length; + + var res = NativeMethods.SendARP(destIp, 0, addr, ref len); + + if (res == 0) + return new ArpRequestResult(new PhysicalAddress(addr)); + return new ArpRequestResult(new Win32Exception(res)); + } + + /// + /// Sendet eine Anfrage über das ARP-Protokoll, um eine IP-Adresse in die Physikalische Adresse aufzulösen. Falls sich die physikalische Adresse bereits im Cache des Hosts befindet, wird diese zurückgegeben. + /// + /// Destination . + /// Ein asynchroner Task, welcher einen ARP-Request sendet. + public static Task SendAsync(IPAddress destination) => Task.Run(() => Send(destination)); + } +} diff --git a/WakeOnLan/ArpRequestResult.cs b/WakeOnLan/ArpRequestResult.cs new file mode 100644 index 0000000..ae5ffd1 --- /dev/null +++ b/WakeOnLan/ArpRequestResult.cs @@ -0,0 +1,57 @@ +using System.Net.NetworkInformation; +using System.Text; + +namespace System.Net +{ + // TODO: rethink the whole exception thing + + /// + /// Enthält die Rückgabewerte der ArpRequest.Send-Funktion. + /// + public class ArpRequestResult + { + /// Falls Fehler bei der Protokollanfrage auftreten, werden diese in dieser Eigenschaft abgelegt. Andernfalls null. + public Exception Exception { get; } + + /// Die aufgelöste physikalische Adresse. + public PhysicalAddress Address { get; } + + /// Erstellt eine neue ArpRequestResult-Instanz + /// Die physikalische Adresse + public ArpRequestResult(PhysicalAddress address) + { + this.Exception = null; + Address = address; + } + + /// Erstellt eine neue ArpRequestResult-Instanz + /// Der aufgetretene Fehler + public ArpRequestResult(Exception exception) + { + this.Exception = exception; + Address = null; + } + + /// Konvertiert ARP-Rückgabewerte in eine Zeichenfolge. + public override string ToString() + { + var sb = new StringBuilder(); + if (Address == null) + sb.Append("no address"); + else + { + sb.Append("address: "); + sb.Append(Address); + } + sb.Append(", "); + if (Exception == null) + sb.Append("no exception"); + else + { + sb.Append("exception: "); + sb.Append(Exception.Message); + } + return sb.ToString(); + } + } +} diff --git a/WakeOnLan/GlobalSuppressions.cs b/WakeOnLan/GlobalSuppressions.cs new file mode 100644 index 0000000..5652ddc --- /dev/null +++ b/WakeOnLan/GlobalSuppressions.cs @@ -0,0 +1,62 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. +// +// To add a suppression to this file, right-click the message in the +// Code Analysis results, point to "Suppress Message", and click +// "In Suppression File". +// You do not need to add suppressions to this file manually. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Byte[],System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Byte[],System.Int32,System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Byte[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Byte[],System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Net.NetworkInformation.PhysicalAddress)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWol(System.Net.IPAddress,System.Net.NetworkInformation.PhysicalAddress,System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Byte[],System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Byte[],System.Int32,System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Byte[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Byte[],System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Net.NetworkInformation.PhysicalAddress)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPAddressExtensions.#SendWolAsync(System.Net.IPAddress,System.Net.NetworkInformation.PhysicalAddress,System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWol(System.Net.IPEndPoint,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWol(System.Net.IPEndPoint,System.Byte[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWol(System.Net.IPEndPoint,System.Net.NetworkInformation.PhysicalAddress)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWol(System.Net.IPEndPoint,System.Byte[],System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWol(System.Net.IPEndPoint,System.Net.NetworkInformation.PhysicalAddress,System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWolAsync(System.Net.IPEndPoint,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte,System.Byte)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWolAsync(System.Net.IPEndPoint,System.Byte[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWolAsync(System.Net.IPEndPoint,System.Net.NetworkInformation.PhysicalAddress)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWolAsync(System.Net.IPEndPoint,System.Byte[],System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.IPEndPointExtensions.#SendWolAsync(System.Net.IPEndPoint,System.Net.NetworkInformation.PhysicalAddress,System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Lan")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "System.Net.Topology.INetMask.#GetMaskBytes()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n", Scope = "member", Target = "System.Net.Topology.NetMask.#op_Equality(System.Net.Topology.NetMask,System.Net.Topology.NetMask)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n", Scope = "member", Target = "System.Net.Topology.NetMask.#op_Inequality(System.Net.Topology.NetMask,System.Net.Topology.NetMask)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n", Scope = "member", Target = "System.Net.Topology.NetMask.#op_BitwiseAnd(System.Net.Topology.NetMask,System.Net.Topology.NetMask)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Scope = "type", Target = "System.Net.Topology.SiblingOptions")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "type", Target = "System.Net.SendWol")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWolAsync(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPEndPoint)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWolAsync(System.Net.NetworkInformation.PhysicalAddress,System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWolAsync(System.Net.NetworkInformation.PhysicalAddress)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWol(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPEndPoint,System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWol(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPEndPoint)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWol(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPAddress,System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWol(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPAddress)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWol(System.Net.NetworkInformation.PhysicalAddress,System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWol(System.Net.NetworkInformation.PhysicalAddress)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n", Scope = "member", Target = "System.Net.Topology.NetMask.#op_BitwiseOr(System.Net.Topology.NetMask,System.Net.Topology.NetMask)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n", Scope = "member", Target = "System.Net.Topology.NetMask.#BitwiseOr(System.Net.Topology.NetMask,System.Net.Topology.NetMask)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWolAsync(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPAddress)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWolAsync(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPAddress,System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Wol", Scope = "member", Target = "System.Net.PhysicalAddressExtensions.#SendWolAsync(System.Net.NetworkInformation.PhysicalAddress,System.Net.IPEndPoint,System.Net.SecureOnPassword)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cidr", Scope = "member", Target = "System.Net.Topology.INetMask.#Cidr")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "m", Scope = "member", Target = "System.Net.Topology.NetMask.#.ctor(System.Byte,System.Byte,System.Byte,System.Byte)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n", Scope = "member", Target = "System.Net.Topology.NetMask.#BitwiseAnd(System.Net.Topology.NetMask,System.Net.Topology.NetMask)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "cidr", Scope = "member", Target = "System.Net.Topology.NetMask.#.ctor(System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "cidr", Scope = "member", Target = "System.Net.Topology.NetMask.#.ctor(System.Byte)")] diff --git a/WakeOnLan/IPAddressExtensions.cs b/WakeOnLan/IPAddressExtensions.cs new file mode 100644 index 0000000..72b049b --- /dev/null +++ b/WakeOnLan/IPAddressExtensions.cs @@ -0,0 +1,166 @@ +using System.Threading.Tasks; +using System.Net.NetworkInformation; +using System.Net.Sockets; + +namespace System.Net +{ + /// Provides extension methods for sending Wake On LAN signals (magic packets) to a specific . + public static class IPAddressExtensions + { + private const int DefaultWolPort = 7; + #region Wol + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The port to send the packet to. + /// The length of the array is not 6. + /// is null. + /// An error occurred when accessing the socket. See Remarks section of for more information. + public static void SendWol(this IPAddress target, byte[] macAddress, int port) => target.SendWol(macAddress, port, null); + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The port to send the packet to. + /// The SecureOn password of the client. + /// The length of the array is not 6. + /// is null. + /// An error occurred when accessing the socket. See Remarks section of for more information. + public static void SendWol(this IPAddress target, byte[] macAddress, int port, SecureOnPassword password) + { + Net.MagicPacket.Send(new IPEndPoint(target, port), macAddress, password); + } + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The length of the array is not 6. + /// is null. + /// An error occurred when accessing the socket. See Remarks section of for more information. + public static void SendWol(this IPAddress target, byte[] macAddress) => target.SendWol(macAddress, null); + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The SecureOn password of the client. + /// The length of the array is not 6. + /// is null. + /// An error occurred when accessing the socket. See Remarks section of for more information. + public static void SendWol(this IPAddress target, byte[] macAddress, SecureOnPassword password) + { + Net.MagicPacket.Send(new IPEndPoint(target, DefaultWolPort), macAddress, password); + } + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The length of the is not 6. + /// is null. + /// An error occurred when accessing the socket. See Remarks section of for more information. + public static void SendWol(this IPAddress target, PhysicalAddress macAddress) => target.SendWol(macAddress, null); + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The SecureOn password of the client. + /// The length of the is not 6. + /// is null. + /// An error occurred when accessing the socket. See Remarks section of for more information. + public static void SendWol(this IPAddress target, PhysicalAddress macAddress, SecureOnPassword password) + { + Net.MagicPacket.Send(new IPEndPoint(target, DefaultWolPort), macAddress, password); + } + + #endregion + #region ARP + + /// + /// Sendet eine Anfrage über das ARP-Protokoll, um eine IP-Adresse in die Physikalische Adresse aufzulösen. Falls sich die physikalische Adresse bereits im Cache des Hosts befindet, wird diese zurückgegeben. + /// + /// Destination . + /// Eine ArpRequestResult-Instanz, welche die Ergebnisse der Anfrage enthält. + public static ArpRequestResult SendArpRequest(this IPAddress destination) => ArpRequest.Send(destination); + + #endregion + #region TAP + + #region Wol + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The port to send the packet to. + /// The length of the array is not 6. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this IPAddress target, byte[] macAddress, int port) => target.SendWolAsync(macAddress, port, null); + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The port to send the packet to. + /// The SecureOn password of the client. + /// The length of the array is not 6. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this IPAddress target, byte[] macAddress, int port, SecureOnPassword password) + { + return Net.MagicPacket.SendAsync(new IPEndPoint(target, port), macAddress, password); + } + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The length of the array is not 6. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this IPAddress target, byte[] macAddress) => target.SendWolAsync(macAddress, null); + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The SecureOn password of the client. + /// The length of the array is not 6. + /// is null. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this IPAddress target, byte[] macAddress, SecureOnPassword password) + { + return Net.MagicPacket.SendAsync(new IPEndPoint(target, DefaultWolPort), macAddress, password); + } + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The length of the is not 6. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this IPAddress target, PhysicalAddress macAddress) => target.SendWolAsync(macAddress, null); + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The SecureOn password of the client. + /// The length of the is not 6. + /// is null. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this IPAddress target, PhysicalAddress macAddress, SecureOnPassword password) + { + return Net.MagicPacket.SendAsync(new IPEndPoint(target, DefaultWolPort), macAddress, password); + } + + #endregion + + #region ARP + + /// Sends a request via ARP to resolve an IP address to aphysical address. If the physical address is already cached, it's cached value is returned. + /// Destination . + /// An asynchronous which sends an ARP request. + public static Task SendArpRequestAsync(this IPAddress destination) => ArpRequest.SendAsync(destination); + + #endregion + + #endregion + } +} diff --git a/WakeOnLan/IPEndPointExtensions.cs b/WakeOnLan/IPEndPointExtensions.cs new file mode 100644 index 0000000..5c5ad52 --- /dev/null +++ b/WakeOnLan/IPEndPointExtensions.cs @@ -0,0 +1,101 @@ +using System.Threading.Tasks; +using System.Net.NetworkInformation; + +namespace System.Net +{ + /// Provides extension methods for sending Wake On LAN signals (magic packets) to a specific . + public static class IPEndPointExtensions + { + #region Wol + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The length of the array macAddress is not 6. + /// is null. + public static void SendWol(this IPEndPoint target, byte[] macAddress) + { + Net.MagicPacket.Send(target, macAddress); + } + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// is null. + public static void SendWol(this IPEndPoint target, PhysicalAddress macAddress) + { + Net.MagicPacket.Send(target, macAddress); + } + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The SecureOn password of the client. + /// The length of the array macAddress is not 6. + /// is null. + public static void SendWol(this IPEndPoint target, byte[] macAddress, SecureOnPassword password) + { + Net.MagicPacket.Send(target, macAddress, password); + } + + /// + /// Sendet ein Wake-On-LAN-Signal an einen Client. + /// + /// Der Ziel-IPEndPoint. + /// The MAC address of the client. + /// The SecureOn password of the client. + /// is null. + public static void SendWol(this IPEndPoint target, PhysicalAddress macAddress, SecureOnPassword password) + { + Net.MagicPacket.Send(target, macAddress, password); + } + + #endregion + #region TAP + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The length of the array macAddress is not 6. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this IPEndPoint target, byte[] macAddress) + { + return Net.MagicPacket.SendAsync(target, macAddress); + } + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this IPEndPoint target, PhysicalAddress macAddress) + { + return Net.MagicPacket.SendAsync(target, macAddress); + } + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The SecureOn password of the client. + /// The length of the array macAddress is not 6. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this IPEndPoint target, byte[] macAddress, SecureOnPassword password) + { + return Net.MagicPacket.SendAsync(target, macAddress, password); + } + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the client. + /// The SecureOn password of the client. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this IPEndPoint target, PhysicalAddress macAddress, SecureOnPassword password) + { + return Net.MagicPacket.SendAsync(target, macAddress, password); + } + #endregion + } +} diff --git a/WakeOnLan/Localization.cs b/WakeOnLan/Localization.cs new file mode 100644 index 0000000..405c9d3 --- /dev/null +++ b/WakeOnLan/Localization.cs @@ -0,0 +1,12 @@ +#define GERMAN + +namespace System.Net +{ + internal static class Localization + { + public const string ArgumentExceptionInvalidMacAddressLength = "Invalid MAC address length."; + public const string ArgumentExceptionInvalidPasswordLength = "Invalid password length."; + + public const string ArgumentExceptionIsNotAValidMadAddress = " is not a valid MAC address."; + } +} diff --git a/WakeOnLan/MagicPacket.cs b/WakeOnLan/MagicPacket.cs new file mode 100644 index 0000000..15aa6b7 --- /dev/null +++ b/WakeOnLan/MagicPacket.cs @@ -0,0 +1,171 @@ +using System.Threading.Tasks; +using System.Net.NetworkInformation; +using System.Net.Sockets; + +namespace System.Net +{ + /// Provides methods for sending Wake On LAN signals (magic packets). + public static class MagicPacket + { + #region Wol + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the designated client. + /// target is null. + /// is null. + /// The length of the array macAddress is not 6. + /// An error occurred when accessing the socket. See Remarks section of for more information. + public static void Send(IPEndPoint target, byte[] macAddress) => Send(target, macAddress, null); + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the designated client. + /// The SecureOn password of the client. + /// is null. + /// is null. + /// The length of the array is not 6. + /// An error occurred when accessing the socket. See Remarks section of for more information. + public static void Send(IPEndPoint target, byte[] macAddress, SecureOnPassword password) + { + if (macAddress == null) + throw new ArgumentNullException(nameof(macAddress)); + + byte[] passwordBuffer = password?.GetPasswordBytes(); + byte[] packet = GetWolPacket(macAddress, passwordBuffer); + SendPacket(target, packet); + } + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the designated client. + /// is null. + /// An error occurred when accessing the socket. See Remarks section of for more information. + public static void Send(IPEndPoint target, PhysicalAddress macAddress) => Send(target, macAddress, null); + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the designated client. + /// The SecureOn password of the client. + /// is null. + /// An error occurred when accessing the socket. See Remarks section of for more information. + public static void Send(IPEndPoint target, PhysicalAddress macAddress, SecureOnPassword password) + { + if (macAddress == null) + throw new ArgumentNullException(nameof(macAddress)); + + byte[] passwordBuffer = password?.GetPasswordBytes(); + byte[] packet = GetWolPacket(macAddress.GetAddressBytes(), passwordBuffer); + SendPacket(target, packet); + } + + private static void SendPacket(IPEndPoint target, byte[] packet) + { + using (var cl = new UdpClient()) + cl.Send(packet, packet.Length, target); + } + + #endregion + #region TAP + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the designated client. + /// is null. + /// is null. + /// The length of the array is not 6. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendAsync(IPEndPoint target, byte[] macAddress) => SendAsync(target, macAddress); + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the designated client. + /// The SecureOn password of the client. + /// is null. + /// is null. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendAsync(IPEndPoint target, byte[] macAddress, SecureOnPassword password) + { + if (target == null) + throw new ArgumentNullException(nameof(target)); + if (macAddress == null) + throw new ArgumentNullException(nameof(macAddress)); + + var passwordBuffer = password?.GetPasswordBytes(); + var packet = GetWolPacket(macAddress, passwordBuffer); + return SendPacketAsync(target, packet); + } + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the designated client. + /// is null. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendAsync(IPEndPoint target, PhysicalAddress macAddress) => SendAsync(target, macAddress, null); + + /// Sends a Wake On LAN signal (magic packet) to a client. + /// Destination . + /// The MAC address of the designated client. + /// The SecureOn password of the client. + /// is null. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendAsync(IPEndPoint target, PhysicalAddress macAddress, SecureOnPassword password) + { + if (target == null) + throw new ArgumentNullException(nameof(target)); + if (macAddress == null) + throw new ArgumentNullException(nameof(macAddress)); + + var passwordBuffer = password?.GetPasswordBytes(); + var p = GetWolPacket(macAddress.GetAddressBytes(), passwordBuffer); + return SendPacketAsync(target, p); + } + + private static Task SendPacketAsync(IPEndPoint target, byte[] packet) + { + var cl = new UdpClient(); + return cl.SendAsync(packet, packet.Length, target).ContinueWith((Task t) => cl.Close()); + } + + #endregion + #region internal + + /// is null. + /// The length of the array is not 6. + /// The length of the array is not 0 or 6. + private static byte[] GetWolPacket(byte[] macAddress, byte[] password) + { + if (macAddress == null) + throw new ArgumentNullException(nameof(macAddress)); + if (macAddress.Length != 6) + throw new ArgumentException(Localization.ArgumentExceptionInvalidMacAddressLength); + + password = password ?? new byte[0]; + if (password.Length != 0 && password.Length != 6) + throw new ArgumentException(Localization.ArgumentExceptionInvalidPasswordLength); + + var packet = new byte[17 * 6 + password.Length]; + + int offset, i; + for (offset = 0; offset < 6; ++offset) + packet[offset] = 0xFF; + + for (offset = 6; offset < 17 * 6; offset += 6) + for (i = 0; i < 6; ++i) + packet[i + offset] = macAddress[i]; + + if (password.Length > 0) + { + for (offset = 16 * 6 + 6; offset < (17 * 6 + password.Length); offset += 6) + for (i = 0; i < 6; ++i) + packet[i + offset] = password[i]; + } + return packet; + } + + #endregion + } +} diff --git a/WakeOnLan/NativeMethods.cs b/WakeOnLan/NativeMethods.cs new file mode 100644 index 0000000..73c4f88 --- /dev/null +++ b/WakeOnLan/NativeMethods.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; +using System.Security; + +namespace System.Net +{ + internal static class NativeMethods + { + private const string IphlpApi = "iphlpapi.dll"; + + [DllImport(IphlpApi, ExactSpelling = true)] + [SecurityCritical] + internal static extern int SendARP(int destinationIp, int sourceIp, byte[] macAddress, ref int physicalAddrLength); + } +} diff --git a/WakeOnLan/PhysicalAddressExtensions.cs b/WakeOnLan/PhysicalAddressExtensions.cs new file mode 100644 index 0000000..4a8ac93 --- /dev/null +++ b/WakeOnLan/PhysicalAddressExtensions.cs @@ -0,0 +1,148 @@ +using System.Threading.Tasks; +using System.Net.NetworkInformation; + +namespace System.Net +{ + /// Provides extension methods for sending Wake On LAN signals (magic packets) using a specific . + public static class PhysicalAddressExtensions + { + #region Wol + + /// Sends a Wake On LAN signal (magic packet) to the broadcast IP address with the physical address. + /// The instance of the physical address that should be used in the magic packet. + /// is null. + public static void SendWol(this PhysicalAddress address) => address.SendWol(IPAddress.Broadcast, null); + + /// Sends a Wake On LAN signal (magic packet) to a specific IP address with the physical address. + /// The instance of the physical address that should be used in the magic packet. + /// Destination . + /// is null. + public static void SendWol(this PhysicalAddress address, IPAddress target) => address.SendWol(target, null); + + /// Sends a Wake On LAN signal (magic packet) to a specific IP address with the physical address. + /// The instance of the physical address that should be used in the magic packet. + /// Destination . + /// The SecureOn password of the client. + /// is null. + public static void SendWol(this PhysicalAddress address, IPAddress target, SecureOnPassword password) + { + if (address == null) + throw new ArgumentNullException(nameof(address)); + + target.SendWol(address.GetAddressBytes(), password); + } + + + /// Sends a Wake On LAN signal (magic packet) to a specific IP end point with the physical address. + /// The instance of the physical address that should be used in the magic packet. + /// Destination . + /// is null. + public static void SendWol(this PhysicalAddress address, IPEndPoint target) => address.SendWol(target, null); + + /// Sends a Wake On LAN signal (magic packet) to a specific IP end point with the physical address. + /// The instance of the physical address that should be used in the magic packet. + /// Destination . + /// The SecureOn password of the client. + /// is null. + public static void SendWol(this PhysicalAddress address, IPEndPoint target, SecureOnPassword password) + { + if (address == null) + throw new ArgumentNullException(nameof(address)); + + target.SendWol(address.GetAddressBytes(), password); + } + + #endregion + #region TAP + + /// Sends a Wake On LAN signal (magic packet) to the broadcast IP address with the physical address. + /// The instance of the physical address that should be used in the magic packet. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + /// is null. + public static Task SendWolAsync(this PhysicalAddress address) + { + if (address == null) + throw new ArgumentNullException(nameof(address)); + + return IPAddress.Broadcast.SendWolAsync(address.GetAddressBytes()); + } + + /// Sends a Wake On LAN signal (magic packet) to a specific IP address with the physical address. + /// The instance of the physical address that should be used in the magic packet. + /// Destination . + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this PhysicalAddress address, IPAddress target) => address.SendWolAsync(target, null); + + + /// Sends a Wake On LAN signal (magic packet) to a specific IP address with the physical address. + /// The instance of the physical address that should be used in the magic packet. + /// Destination . + /// The SecureOn password of the client. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this PhysicalAddress address, IPAddress target, SecureOnPassword password) + { + if (address == null) + throw new ArgumentNullException(nameof(address)); + + return target.SendWolAsync(address.GetAddressBytes(), password); + } + + /// Sends a Wake On LAN signal (magic packet) to a specific IP end point with the physical address. + /// The instance of the physical address that should be used in the magic packet. + /// Destination . + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this PhysicalAddress address, IPEndPoint target) => address.SendWolAsync(target, null); + + /// Sends a Wake On LAN signal (magic packet) to a specific IP end point with the physical address. + /// The instance of the physical address that should be used in the magic packet. + /// Destination . + /// The SecureOn password of the client. + /// is null. + /// An asynchronous which sends a Wake On LAN signal (magic packet) to a client. + public static Task SendWolAsync(this PhysicalAddress address, IPEndPoint target, SecureOnPassword password) + { + if (address == null) + throw new ArgumentNullException(nameof(address)); + + return target.SendWolAsync(address.GetAddressBytes(), password); + } + + #endregion + #region common + + /// Gets the type () of the . + /// The address. + /// is null. + /// The of the physical addess (MAC address). + public static PhysicalAddressType GetAddressType(this PhysicalAddress address) + { + if (address == null) + throw new ArgumentNullException(nameof(address)); + + var bytes = address.GetAddressBytes(); + if (bytes == null || bytes.Length < 1) + throw new ArgumentException($"Invalid {nameof(address)}."); + return (bytes[0] & 0x1) == 0 ? PhysicalAddressType.Unicast : PhysicalAddressType.Multicast; + } + + /// Gets the administrator () of the . + /// The address. + /// is null. + /// The of the physical addess (MAC address). + public static PhysicalAddressAdministrator GetAddressAdministrator(this PhysicalAddress address) + { + if (address == null) + throw new ArgumentNullException(nameof(address)); + + var bytes = address.GetAddressBytes(); + if (bytes == null || bytes.Length < 1) + throw new ArgumentException($"Invalid {nameof(address)}."); + return (bytes[0] & 0x2) == 0 ? PhysicalAddressAdministrator.Global : PhysicalAddressAdministrator.Local; + } + + #endregion + } +} diff --git a/WakeOnLan/PhysicalAddressProperties.cs b/WakeOnLan/PhysicalAddressProperties.cs new file mode 100644 index 0000000..837f5f7 --- /dev/null +++ b/WakeOnLan/PhysicalAddressProperties.cs @@ -0,0 +1,34 @@ +namespace System.Net +{ + /// + /// Der Administrator der physikalischen Adresse gibt an, ob die Adresse globally unique oder local administrated ist. + /// + public enum PhysicalAddressAdministrator + { + /// + /// Die Adresse ist global einzigartig (nach der OUI). + /// + Global, + + /// + /// Die Adresse ist lokal administriert. + /// + Local + } + + /// + /// Der Typ der physikalischen Adresse gibt an, ob es um eine UNicast oder Multicast-Adresse handelt. + /// + public enum PhysicalAddressType + { + /// + /// Bezeichnet eine Unicast-Adresse. + /// + Unicast, + + /// + /// Bezeichnet eine Multicast-Adresse + /// + Multicast + } +} diff --git a/WakeOnLan/Properties/AssemblyInfo.cs b/WakeOnLan/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..97813fb --- /dev/null +++ b/WakeOnLan/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; +using System; + +[assembly: InternalsVisibleTo("WakeOnLan.Tests")] diff --git a/WakeOnLan/SecureOnPassword.cs b/WakeOnLan/SecureOnPassword.cs new file mode 100644 index 0000000..d159dab --- /dev/null +++ b/WakeOnLan/SecureOnPassword.cs @@ -0,0 +1,95 @@ +namespace System.Net +{ + /// Provides a SecureOn password. + [Serializable] + public sealed class SecureOnPassword + { + private readonly byte[] _password; + + /// Initializes a new instance of with the given password. + /// The password as array. + /// is null. + /// The length of the array password is not 6. + public SecureOnPassword(byte[] password) + { + if (password == null) + throw new ArgumentNullException(nameof(password)); + if (password.Length != 6) + throw new ArgumentException(Localization.ArgumentExceptionInvalidPasswordLength); + _password = password; + } + + /// Gets the buffer of the password. + public byte[] GetPasswordBytes() + { + if (_password == null) + return null; + var buffer = new byte[_password.Length]; + Array.Copy(_password, buffer, 0); + return buffer; + } + + /// Initializes a new instance of with the given password. + /// The password as . + /// is null. + /// Uses as encoding. + public SecureOnPassword(string password) + : this(password, Text.Encoding.Default) + { } + + + /// Initializes a new instance of with the given password. + /// The password as . + /// The instance to use for the password. + /// is null. + /// is null. + /// The array wich is created using the password has more elements than 6. + public SecureOnPassword(string password, Text.Encoding encoding) + { + if (password == null) + throw new ArgumentNullException(nameof(password)); + if (encoding == null) + throw new ArgumentNullException(nameof(encoding)); + + if (string.IsNullOrEmpty(password)) + _password = new byte[6]; + + var bytes = encoding.GetBytes(password); + if (bytes.Length > 6) + throw new ArgumentException(Localization.ArgumentExceptionInvalidPasswordLength); + + _password = new byte[6]; + for (int i = 0; i < bytes.Length; i++) + _password[i] = bytes[i]; + if (bytes.Length < 6) + { + for (int i = bytes.Length - 1; i < 6; i++) + _password[i] = 0x00; + } + } + + /// Converts the to dash notation. + /// A representing the as dash notation. + public override string ToString() => ToString("X2"); + + /// Converts the to dash notation. + /// A representing the as dash notation. + private string ToString(string format) + { + var f = new string[6]; + for (int i = 0; i < f.Length; i++) + f[i] = _password[i].ToString(format); + return string.Join("-", f); + } + + /// Converts the to dash notation. + /// A representing the as dash notation. + public string ToString(IFormatProvider format) + { + var f = new string[6]; + for (int i = 0; i < f.Length; i++) + f[i] = _password[i].ToString(format); + return string.Join("-", f); + } + } +} diff --git a/WakeOnLan/Topology/ByteArrayExtensions.cs b/WakeOnLan/Topology/ByteArrayExtensions.cs new file mode 100644 index 0000000..5f33979 --- /dev/null +++ b/WakeOnLan/Topology/ByteArrayExtensions.cs @@ -0,0 +1,287 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace System.Net.Topology +{ + internal static class ByteArrayExtensions + { + internal static IEnumerable ToBitStream(this byte[] bytes, bool fromLeft) + { + if (bytes == null) + throw new ArgumentNullException(nameof(bytes)); + + if (fromLeft) + { + for (int i = 0; i < bytes.Length; ++i) + { + byte tmp = bytes[i].ReverseBits(); + for (int j = 0; j < 8; ++j) + { + yield return (tmp & 1) == 1; + tmp >>= 1; + } + } + } + else + { + for (int i = bytes.Length - 1; i >= 0; --i) + { + byte tmp = bytes[i]; + for (int j = 0; j < 8; ++j) + { + yield return (tmp & 1) == 1; + tmp >>= 1; + } + } + } + } + + private static int CountFromSide(byte[] bits, bool value, bool fromleft) + { + int counter = 0; + var str = bits.ToBitStream(fromleft); + foreach (var bit in str) + { + if (bit == value) + ++counter; + else return counter; + } + return counter; + } + + internal static int CountFromLeft(this byte[] bits, bool value) + { + if (bits == null) + throw new ArgumentNullException(nameof(bits)); + return CountFromSide(bits, value, true); + } + + internal static int CountFromRight(this byte[] bits, bool value) + { + if (bits == null) + throw new ArgumentNullException(nameof(bits)); + return CountFromSide(bits, value, false); + } + + internal static string ToBinaryString(this byte[] bits, char separator) + { + if (bits == null) + throw new ArgumentNullException(nameof(bits)); + + const int radix = 2; + const int padding = 8; + const char paddingChar = '0'; + + var sb = new StringBuilder(); + sb.Append(Convert.ToString(bits[0], radix).PadLeft(padding, paddingChar)).Append(separator); + sb.Append(Convert.ToString(bits[1], radix).PadLeft(padding, paddingChar)).Append(separator); + sb.Append(Convert.ToString(bits[2], radix).PadLeft(padding, paddingChar)).Append(separator); + sb.Append(Convert.ToString(bits[3], radix).PadLeft(padding, paddingChar)); + return sb.ToString(); + } + + internal static string ToBinaryString(this byte[] bits) + { + if (bits == null) + throw new ArgumentNullException(nameof(bits)); + + const int radix = 2; + const int padding = 8; + const char paddingChar = '0'; + + var sb = new StringBuilder(); + sb.Append(Convert.ToString(bits[0], radix).PadLeft(padding, paddingChar)); + sb.Append(Convert.ToString(bits[1], radix).PadLeft(padding, paddingChar)); + sb.Append(Convert.ToString(bits[2], radix).PadLeft(padding, paddingChar)); + sb.Append(Convert.ToString(bits[3], radix).PadLeft(padding, paddingChar)); + return sb.ToString(); + } + + internal static bool RepresentsValidNetMask(this byte[] bits) + { + if (bits == null) + throw new ArgumentNullException(nameof(bits)); + + int fromLeft = bits.CountFromLeft(true); + int fromRight = bits.CountFromRight(false); + + // Sum of all counted indexes schloud be equal the whole length + return (fromLeft + fromRight) == (8 * NetMask.MaskLength); + } + + internal static byte[] And(this byte[] b1, byte[] b2) + { + if (b1 == null) + throw new ArgumentNullException(nameof(b1)); + if (b2 == null) + throw new ArgumentNullException(nameof(b2)); + + if (b1.Length == 1 && b2.Length == 1) + { + int ib1 = b1[0]; + int ib2 = b2[0]; + return new[] { (byte)(ib1 & ib2) }; + } + if (b1.Length == 2 && b2.Length == 2) + { + var sb1 = BitConverter.ToInt16(b1, 0); + var sb2 = BitConverter.ToInt16(b2, 0); + return BitConverter.GetBytes((short)(sb1 & sb2)); + } + if (b1.Length == 4 && b2.Length == 4) + { + var ib1 = BitConverter.ToInt32(b1, 0); + var ib2 = BitConverter.ToInt32(b2, 0); + return BitConverter.GetBytes(ib1 & ib2); + } + if (b1.Length != b2.Length) + { + // Or maybe throw exception? + + int maxIndex = Math.Max(b1.Length, b2.Length); + byte[] biggerArray = b1.Length > b2.Length ? b1 : b2; + byte[] smallerArray = b1.Length <= b2.Length ? b1 : b2; + + var paddedArray = new byte[maxIndex]; + + Buffer.BlockCopy(smallerArray, 0, paddedArray, 0, smallerArray.Length); + + Debug.Assert(biggerArray.Length == paddedArray.Length); + + return And(biggerArray, paddedArray); + } + + var targetIndex = b1.Length; + var andedArray = new byte[targetIndex]; + Buffer.BlockCopy(b1, 0, andedArray, 0, andedArray.Length); + + for (int i = 0; i < targetIndex; ++i) + andedArray[i] &= b2[i]; + + return andedArray; + } + + internal static byte[] Or(this byte[] b1, byte[] b2) + { + if (b1 == null) + throw new ArgumentNullException(nameof(b1)); + if (b2 == null) + throw new ArgumentNullException(nameof(b2)); + + if (b1.Length == 1 && b2.Length == 1) + { + int ib1 = b1[0]; + int ib2 = b2[0]; + return new[] { (byte)(ib1 | ib2) }; + } + if (b1.Length == 2 && b2.Length == 2) + { + var sb1 = BitConverter.ToInt16(b1, 0); + var sb2 = BitConverter.ToInt16(b2, 0); + return BitConverter.GetBytes((short)(sb1 | sb2)); + } + if (b1.Length == 4 && b2.Length == 4) + { + var ib1 = BitConverter.ToInt32(b1, 0); + var ib2 = BitConverter.ToInt32(b2, 0); + return BitConverter.GetBytes(ib1 | ib2); + } + if (b1.Length != b2.Length) + { + // Or maybe throw exception? + + int maxIndex = Math.Max(b1.Length, b2.Length); + byte[] biggerArray = b1.Length > b2.Length ? b1 : b2; + byte[] smallerArray = b1.Length <= b2.Length ? b1 : b2; + + var paddedArray = new byte[maxIndex]; + + Buffer.BlockCopy(smallerArray, 0, paddedArray, 0, smallerArray.Length); + + Debug.Assert(biggerArray.Length == paddedArray.Length); + + return Or(biggerArray, paddedArray); + } + + var targetIndex = b1.Length; + var oredArray = new byte[targetIndex]; + Buffer.BlockCopy(b1, 0, oredArray, 0, oredArray.Length); + + for (int i = 0; i < targetIndex; ++i) + oredArray[i] |= b2[i]; + + return oredArray; + } + + internal static byte[] Xor(this byte[] b1, byte[] b2) + { + if (b1 == null) + throw new ArgumentNullException(nameof(b1)); + if (b2 == null) + throw new ArgumentNullException(nameof(b2)); + + // TODO: Testing + + if (b1.Length == 1 && b2.Length == 1) + { + int ib1 = b1[0]; + int ib2 = b2[0]; + return new[] { (byte)(ib1 ^ ib2) }; + } + if (b1.Length == 2 && b2.Length == 2) + { + var sb1 = BitConverter.ToInt16(b1, 0); + var sb2 = BitConverter.ToInt16(b2, 0); + return BitConverter.GetBytes((short)(sb1 ^ sb2)); + } + if (b1.Length == 4 && b2.Length == 4) + { + var ib1 = BitConverter.ToInt32(b1, 0); + var ib2 = BitConverter.ToInt32(b2, 0); + return BitConverter.GetBytes(ib1 ^ ib2); + } + if (b1.Length != b2.Length) + { + // Or maybe throw exception? + + int maxIndex = Math.Max(b1.Length, b2.Length); + byte[] biggerArray = b1.Length > b2.Length ? b1 : b2; + byte[] smallerArray = b1.Length <= b2.Length ? b1 : b2; + + var paddedArray = new byte[maxIndex]; + + Buffer.BlockCopy(smallerArray, 0, paddedArray, 0, smallerArray.Length); + + Debug.Assert(biggerArray.Length == paddedArray.Length); + + return Xor(biggerArray, paddedArray); + } + + var targetIndex = b1.Length; + var xoredArray = new byte[targetIndex]; + Buffer.BlockCopy(b1, 0, xoredArray, 0, xoredArray.Length); + + for (int i = 0; i < targetIndex; ++i) + xoredArray[i] ^= b2[i]; + + return xoredArray; + } + + internal static byte[] Not(this byte[] bits) + { + if (bits == null) + throw new ArgumentNullException(nameof(bits)); + + if (bits.Length == 4) + { + var i = BitConverter.ToInt32(bits, 0); + return BitConverter.GetBytes(~i); + } + var newBytes = new byte[bits.Length]; + for (int i = 0; i < newBytes.Length; ++i) + newBytes[i] = unchecked((byte)(~bits[i])); + return newBytes; + } + } +} diff --git a/WakeOnLan/Topology/ByteExtensions.cs b/WakeOnLan/Topology/ByteExtensions.cs new file mode 100644 index 0000000..403e94a --- /dev/null +++ b/WakeOnLan/Topology/ByteExtensions.cs @@ -0,0 +1,43 @@ + +namespace System.Net.Topology +{ + internal static class ByteExtensions + { + public static byte[] BitReverseTable = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff + }; + + public static byte ReverseBits(this byte toReverse) => BitReverseTable[toReverse]; + } +} diff --git a/WakeOnLan/Topology/INetMask.cs b/WakeOnLan/Topology/INetMask.cs new file mode 100644 index 0000000..6413a37 --- /dev/null +++ b/WakeOnLan/Topology/INetMask.cs @@ -0,0 +1,17 @@ + +namespace System.Net.Topology +{ + /// Provides an interface for IP net masks. + public interface INetMask + { + /// Gets the length of the net mask in bits. + int AddressLength { get; } + + /// Gets the amount of set bits from the left side (used in CIDR-Notation of net masks). + int Cidr { get; } + + /// Gets the bits of the net mask instance as an BitArray object instance. + /// The bits of the net mask instance as an BitArray object instance + byte[] GetMaskBytes(); + } +} diff --git a/WakeOnLan/Topology/IPAddressExtensions.cs b/WakeOnLan/Topology/IPAddressExtensions.cs new file mode 100644 index 0000000..4919f45 --- /dev/null +++ b/WakeOnLan/Topology/IPAddressExtensions.cs @@ -0,0 +1,167 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Net.Topology +{ + /// Provides extension methods for the . + public static class IPAddressExtensions + { + private const string OnlyIPv4Supported = "Only IPv4 is currently supported"; + + /// Enumerates through the siblings of an in a network. Compliant to RFC 950 (2^n-2). + /// The address + /// The net mask of the network + public static IEnumerable GetSiblings(this IPAddress address, NetMask mask) + { + return GetSiblings(address, mask, SiblingOptions.ExcludeUnusable); + } + + /// Enumerates through the siblings of an in a network. + /// The address + /// The net mask of the network + /// Options which addresses to include an which not + public static IEnumerable GetSiblings(this IPAddress address, NetMask mask, SiblingOptions options) + { + if (address == null) + throw new ArgumentNullException(nameof(address)); + if (mask == null) + throw new ArgumentNullException(nameof(mask)); + if (address.AddressFamily != Sockets.AddressFamily.InterNetwork) + throw new NotSupportedException(OnlyIPv4Supported); + + bool includeSelf = BitHelper.IsOptionSet(options, SiblingOptions.IncludeSelf); + bool includeBroadcast = BitHelper.IsOptionSet(options, SiblingOptions.IncludeBroadcast); + bool includeNetworkIdentifier = BitHelper.IsOptionSet(options, SiblingOptions.IncludeNetworkIdentifier); + + bool alreadyReturnedSelf = false; + + var netPrefix = address.GetNetworkPrefix(mask); + + if (includeNetworkIdentifier) + { + netPrefix = address.GetNetworkPrefix(mask); + if (netPrefix.Equals(address)) + alreadyReturnedSelf = true; + yield return netPrefix; + } + + var selfAddressBytes = address.GetAddressBytes(); + + var netPrefixBytes = netPrefix.GetAddressBytes(); + + int cidr = mask.Cidr; + uint maxHosts = 0xFFFFFFFF; + + if (cidr > 0) + maxHosts = (uint)(1 << (8 * NetMask.MaskLength - cidr)) - 1; + + var hostBytes = new byte[NetMask.MaskLength]; + for (int hostPart = 1; hostPart < maxHosts; ++hostPart) + { + unchecked + { + hostBytes[0] = (byte)(hostPart >> 24); + hostBytes[1] = (byte)(hostPart >> 16); + hostBytes[2] = (byte)(hostPart >> 8); + hostBytes[3] = (byte)(hostPart >> 0); + } + + Debug.WriteLine("HostPart: " + hostPart.ToString("X2").PadLeft(8, '0') + " (" + BitConverter.ToString(hostBytes) + ")"); + + var nextIpBytes = netPrefixBytes.Or(hostBytes); + var nextIp = new IPAddress(nextIpBytes); + + if (!alreadyReturnedSelf) + { + if (includeSelf) + { + if (nextIpBytes[0] == selfAddressBytes[0] + && nextIpBytes[1] == selfAddressBytes[1] + && nextIpBytes[2] == selfAddressBytes[2] + && nextIpBytes[3] == selfAddressBytes[3]) + alreadyReturnedSelf = true; + yield return nextIp; + } + else if (nextIpBytes[0] != selfAddressBytes[0] + || nextIpBytes[1] != selfAddressBytes[1] + || nextIpBytes[2] != selfAddressBytes[2] + || nextIpBytes[3] != selfAddressBytes[3]) + yield return nextIp; + } + else + yield return nextIp; + } + + if (includeBroadcast) + { + var broadcastAddress = address.GetBroadcastAddress(mask); + if (!address.Equals(broadcastAddress) || (address.Equals(broadcastAddress) && !alreadyReturnedSelf)) + yield return broadcastAddress; + } + } + + /// Gets the network prefix of an . + /// The address + /// The net mask of the network + /// The network prefix of an + public static IPAddress GetNetworkPrefix(this IPAddress address, NetMask mask) + { + if (address == null) + throw new ArgumentNullException(nameof(address)); + if (mask == null) + throw new ArgumentNullException(nameof(mask)); + + if (address.AddressFamily != Sockets.AddressFamily.InterNetwork) + throw new NotSupportedException(OnlyIPv4Supported); + + return mask & address; + } + + /// Gets the broadcast address of an . + /// The address + /// The net mask of the network + /// The broadcast address of an + public static IPAddress GetBroadcastAddress(this IPAddress address, NetMask mask) + { + if (address == null) + throw new ArgumentNullException("address"); + if (mask == null) + throw new ArgumentNullException("mask"); + + if (address.AddressFamily != Sockets.AddressFamily.InterNetwork) + throw new NotSupportedException(OnlyIPv4Supported); + + // TODO: Test + + var ipBytes = address.GetAddressBytes(); + var notMaskBytes = mask.GetMaskBytes().Not(); + + var broadcastAddressBytes = notMaskBytes.Or(ipBytes); + return new IPAddress(broadcastAddressBytes); + } + + /// Gets the host identifier (rest) an . + /// The address + /// The net mask of the network + /// The host identifier (rest) an + public static IPAddress GetHostIdentifier(this IPAddress address, NetMask mask) + { + if (address == null) + throw new ArgumentNullException(nameof(address)); + if (mask == null) + throw new ArgumentNullException(nameof(mask)); + if (address.AddressFamily != Sockets.AddressFamily.InterNetwork) + throw new NotSupportedException(OnlyIPv4Supported); + + var maskBits = mask.GetMaskBytes(); + var ipBits = address.GetAddressBytes(); + + // ~Mask & IP + var retVal = maskBits.Not().And(ipBits); + var bytes = new byte[NetMask.MaskLength]; + Buffer.BlockCopy(retVal, 0, bytes, 0, bytes.Length); + + return new IPAddress(bytes); + } + } +} diff --git a/WakeOnLan/Topology/NetMask.cs b/WakeOnLan/Topology/NetMask.cs new file mode 100644 index 0000000..7706aac --- /dev/null +++ b/WakeOnLan/Topology/NetMask.cs @@ -0,0 +1,312 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + +namespace System.Net.Topology +{ + // TODO: Use Span when available (instead of byte arrays) + /// Represents an IPv4 net mask. + [StructLayout(LayoutKind.Explicit)] + public struct NetMask : INetMask, IEquatable + { + [FieldOffset(0)] + private readonly uint _mask; + + #region byte-wise fields + + // We are assuming here, that the byte-ordering is like on a normal x86 system + + [FieldOffset(3)] + private readonly byte _maskB0; + [FieldOffset(2)] + private readonly byte _maskB1; + [FieldOffset(1)] + private readonly byte _maskB2; + [FieldOffset(0)] + private readonly byte _maskB3; + + #endregion + + internal const int MaskLength = 4; + + /// Represents an empty IPv4 NetMask (all bits set to 0). + public static NetMask Empty { get; } = new NetMask(); + + /// Gets the length of the net mask in bits. + public int AddressLength => MaskLength * 8; + + /// Gets the amount of set bits from the left side (used in CIDR-Notation of net masks). + public int Cidr => GetCidr(_mask); + + #region Ctors + + /// Creates a new instance of cloning an existing instance of . + public NetMask(NetMask mask) + { + if (mask == null) + throw new ArgumentNullException(nameof(mask)); + _maskB0 = _maskB1 = _maskB2 = _maskB3 = 0; + _mask = mask._mask; + } + + /// Creates a new instance of from an array of . + public NetMask(byte[] value) + { + _maskB0 = _maskB1 = _maskB2 = _maskB3 = 0; + _mask = 0; + if (value == null || value.Length == 0) // maybe throw ArgumentNullException? + return; + + if (value.Length != MaskLength) + throw new ArgumentException("Invalid mask length."); + + CheckMaskBytes(value); // check if passed mask are a valid mask. if not, throw Exception + _maskB0 = value[0]; + _maskB1 = value[1]; + _maskB2 = value[2]; + _maskB3 = value[3]; + } + + /// Creates a new instance of from a given . + /// The IPv4 address. + public NetMask(IPAddress address) + : this(address == null ? null : address.GetAddressBytes()) + { } + + /// Creates a new instance of . + /// The first byte. + /// The second byte. + /// The third byte. + /// The fourth byte. + public NetMask(byte m0, byte m1, byte m2, byte m3) + : this(new[] { m0, m1, m2, m3 }) + { } + + /// Creates a new instance of . + /// The mask represented by the CIDR notation integer. + public NetMask(byte cidr) + { + // maybe change parameter type interpretation to CIDR? + if (cidr > MaskLength * 8) + throw new ArgumentException("Invalid CIDR length"); + _maskB0 = _maskB1 = _maskB2 = _maskB3 = 0; // to keep init checks happy + _mask = GetUIntFromCidrValue(cidr); + } + + private NetMask(uint ipv4Mask) + { + _maskB0 = _maskB1 = _maskB2 = _maskB3 = 0; // to keep init checks happy + _mask = ipv4Mask; + } + + #endregion + + private static void CheckMaskBytes(byte[] bytes) + { + if (!bytes.RepresentsValidNetMask()) + throw new ArgumentException("The passed mask do not represent a valid net mask."); + } + + /// Gets the bits of the net mask instance as an BitArray object instance. + /// The bits of the net mask instance as an BitArray object instance. + public byte[] GetMaskBytes() => new[] { _maskB0, _maskB1, _maskB2, _maskB3 }; + + private static int GetCidr(uint mask) + { + Debug.Assert(sizeof(uint) == MaskLength); + return mask.CountOnesFromLeft(); + } + + /// Extends the current instance by a given value (CIDR-wise). + /// The mask to use as a reference. + /// The value. + /// Because is a reference type, this method is static. If it were not like this, you could change the value of .Empty, for example. + public static NetMask Extend(NetMask mask, int value) + { + int currentCidr = mask.Cidr; + if (currentCidr >= MaskLength * 8) + return new NetMask(mask); + if (currentCidr <= 0) + currentCidr = 0; + + int newLength = Math.Max(Math.Min(currentCidr + value, 32), 0); + + var m = BytesFromCidrValue(newLength); + + return new NetMask(m); + } + + /// Abbreviates the current instance by a given value (CIDR-wise). + /// The mask to use as a reference. + /// The value. + /// Because is a reference type, this method is static. If it were not like this, you could change the value of .Empty, for example. + public static NetMask Abbreviate(NetMask mask, int value) + { + int currentCidr = mask.Cidr; + if (currentCidr < 1) + return new NetMask(mask); + if (currentCidr >= MaskLength * 8) + currentCidr = MaskLength * 8; + + int newLength = Math.Max(Math.Min(currentCidr - value, 32), 0); + + var m = BytesFromCidrValue(newLength); + return new NetMask(m); + } + + /// Returns a value indicating whether the given array of represents a valid net mask. + /// True if the given array of represents a valid net mask, otherwise false. + public static bool GetIsValidNetMask(byte[] mask) => mask.RepresentsValidNetMask(); + + // TODO: Testing(!) + [Obsolete] + private static byte[] BytesFromCidrValue(int cidr) + { + int target = MaskLength * 8 - cidr; + int mask = 0; + for (int i = 0; i < target; ++i) + { + mask >>= 1; + mask |= unchecked((int)0x80000000); + } + var bytes = BitConverter.GetBytes(~mask); + return new[] { + bytes[0].ReverseBits(), + bytes[1].ReverseBits(), + bytes[2].ReverseBits(), + bytes[3].ReverseBits() + }; + } + + // TODO: Testing(!) + private static uint GetUIntFromCidrValue(int cidr) => UIntExtensions.CreateWithOnesFromLeft(cidr); + + #region Operators + + #region Equality + + /// Returns a other indicating whether two instances of are equal. + /// The first other to compare. + /// The second other to compare. + /// true if and are equal; otherwise, false. + public static bool operator ==(NetMask n1, NetMask n2) + { + if (ReferenceEquals(n1, n2)) + return true; + if (((object)n1 == null) || ((object)n2 == null)) + return false; + return n1.Equals(n2); + } + + /// Returns a other indicating whether two instances of are not equal. + /// The first other to compare. + /// The second other to compare. + /// true if and are not equal; otherwise, false. + public static bool operator !=(NetMask n1, NetMask n2) => !(n1 == n2); // Problem solved + + #endregion + #region And + + /// Bitwise combines a instance and an the AND operation. + /// The net mask. + /// The IPAddress. + /// The bitwised combination using the AND operation. + public static IPAddress operator &(IPAddress address, NetMask mask) => mask & address; + + /// Bitwise combines a instance and an the AND operation. + /// The net mask. + /// The IPAddress. + /// The bitwised combination using the AND operation. + public static IPAddress BitwiseAnd(IPAddress address, NetMask mask) => mask & address; + + /// Bitwise combines a instance and an the AND operation. + /// The net mask. + /// The IPAddress. + /// The bitwised combination using the AND operation. + public static IPAddress operator &(NetMask mask, IPAddress address) + { + var ipBytes = address == null + ? new byte[MaskLength] + : address.GetAddressBytes(); + var maskBytes = mask == null + ? new byte[MaskLength] + : new byte[] { mask._maskB0, mask._maskB1, mask._maskB2, mask._maskB3 }; + + byte[] combinedBytes = maskBytes.And(ipBytes); + return new IPAddress(combinedBytes); + } + + /// Bitwise combines a instance and an the AND operation. + /// The net mask. + /// The IPAddress. + /// The bitwised combination using the AND operation. + public static IPAddress BitwiseAnd(NetMask mask, IPAddress address) => mask & address; + + /// Bitwise combines the two instances of using the AND operation. + /// The first other. + /// The second other. + /// The bitwised combination using the AND operation. + public static NetMask operator &(NetMask n1, NetMask n2) => new NetMask(n1._mask & n2._mask); + + /// Bitwise combines the two instances of using the AND operation. + /// The first other. + /// The second other. + /// The bitwised combination using the AND operation. + public static NetMask BitwiseAnd(NetMask n1, NetMask n2) => n1 & n2; + + #endregion + #region Or + + /// Bitwise combines the two instances of using the OR operation. + /// The first other. + /// The second other. + /// The bitwised combination using the OR operation. + public static NetMask operator |(NetMask n1, NetMask n2) => new NetMask(n1._mask | n2._mask); + + /// Bitwise combines the two instances of using the OR operation. + /// The first other. + /// The second other. + /// The bitwised combination using the OR operation. + public static NetMask BitwiseOr(NetMask n1, NetMask n2) => n1 | n2; + + #endregion + + #endregion + #region Common overrides + + /// Converts the other of this instance to its equivalent string representation. + /// A string that represents the other of this instance. + /// 1 + public override string ToString() + { + var sb = new StringBuilder(4 * 3 + 3 * 3 + 32 + 3 + 3); // 255.255.255.255 (11111111111111111111111111111111) + + var arr = new byte[] { _maskB0, _maskB1, _maskB2, _maskB3 }; + var asString = arr.ToBinaryString('.'); + + sb.Append(arr[0]).Append('.').Append(arr[1]).Append('.').Append(arr[2]).Append('.').Append(arr[3]).Append(" ("); + sb.Append(asString).Append(')'); + + return sb.ToString(); + } + + /// Returns a value indicating whether this instance and a specified represent the same type and other. + /// true if is a and equal to this instance; otherwise, false. + /// The object to compare with this instance. + /// 2 + public override bool Equals(object obj) => obj != null && obj is NetMask && Equals((NetMask)obj); + + /// Returns a value indicating whether this instance and a specified object represent the same other. + /// true if is equal to this instance; otherwise, false. + /// An object to compare to this instance. + /// 2 + public bool Equals(NetMask other) => other._mask == _mask; + + /// Returns the hash code for this instance. + /// A 32-bit signed integer hash code. + /// 2 + public override int GetHashCode() => unchecked((int)_mask); + + #endregion + } +} diff --git a/WakeOnLan/Topology/NetMaskExtensions.cs b/WakeOnLan/Topology/NetMaskExtensions.cs new file mode 100644 index 0000000..6b12ae7 --- /dev/null +++ b/WakeOnLan/Topology/NetMaskExtensions.cs @@ -0,0 +1,36 @@ + +namespace System.Net.Topology +{ + /// Provides extension methods for the . + public static class NetMaskExtensions + { + /// Gets the number of siblings an can have in a given network. Compliant to RFC 950 (2^n-2). + /// The net mask of the network + /// The number of siblings an can have in the given network. + public static int GetSiblingCount(this NetMask mask) => GetSiblingCount(mask, SiblingOptions.ExcludeUnusable); + + /// Gets the number of siblings an can have in a given network. + /// The net mask of the network + /// Options which addresses to include an which not + /// The number of siblings an can have in the given network. + public static int GetSiblingCount(this NetMask mask, SiblingOptions options) + { + if (mask == null) + throw new ArgumentNullException(nameof(mask)); + + bool includeSelf = BitHelper.IsOptionSet(options, SiblingOptions.IncludeSelf); + bool includeBroadcast = BitHelper.IsOptionSet(options, SiblingOptions.IncludeBroadcast); + bool includeNetworkIdentifier = BitHelper.IsOptionSet(options, SiblingOptions.IncludeNetworkIdentifier); + + var hostPartBits = mask.GetMaskBytes().CountFromRight(false); + var total = 1 << hostPartBits; + total -= includeSelf ? 0 : 1; + total -= includeBroadcast ? 0 : 1; + total -= includeNetworkIdentifier ? 0 : 1; + + // TODO: Testing + + return total; + } + } +} diff --git a/WakeOnLan/Topology/SiblingOptions.cs b/WakeOnLan/Topology/SiblingOptions.cs new file mode 100644 index 0000000..5a84476 --- /dev/null +++ b/WakeOnLan/Topology/SiblingOptions.cs @@ -0,0 +1,27 @@ +namespace System.Net.Topology +{ + /// Provides options for doing network sibling calculations using a net mask. + [Flags] + public enum SiblingOptions + { + /// Do not include the broadcast or net address neither the addess passed to the method. + ExcludeAll = 0, + /// Include the addess passed to the method. + IncludeSelf = 1, + /// Include the addess passed to the method. Compliant to RFC 950 (2^n-2). + ExcludeUnusable = IncludeSelf, + /// Include the broadcast address. + IncludeBroadcast = 2, + /// Include the net address. + IncludeNetworkIdentifier = 4, + /// Include all addresses possible. RFC 1878 (2^n). + IncludeAll = IncludeSelf | IncludeBroadcast | IncludeNetworkIdentifier + } + + internal static class BitHelper + { + [Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] // as if NGen will ever compile this assembly ;) + [Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + internal static bool IsOptionSet(SiblingOptions value, SiblingOptions testValue) => (value & testValue) == testValue; + } +} diff --git a/WakeOnLan/Topology/StringExtensions.cs b/WakeOnLan/Topology/StringExtensions.cs new file mode 100644 index 0000000..e506670 --- /dev/null +++ b/WakeOnLan/Topology/StringExtensions.cs @@ -0,0 +1,39 @@ + +namespace System.Net.Topology +{ + internal static class StringExtensions + { + // TODO: The following method uses unsafe stuff. We don't want to pass the /unsafe flag to the compiler, so we use a simple, safe method instead. + // As soon as .NET Standard with Span and the new String.Create is available, we can make this faster and safe. + /* + internal static unsafe string Reverse(this string input) + { + int len = input.Length; + + // Why allocate a char[] array on the heap when you won't use it + // outside of this method? Use the stack. + char* reversed = stackalloc char[len]; + + // Avoid bounds-checking performance penalties. + fixed (char* str = input) + { + int i = 0; + int j = i + len - 1; + while (i < len) + reversed[i++] = str[j--]; + } + + // Need to use this overload for the System.String constructor + // as providing just the char* pointer could result in garbage + // at the end of the string (no guarantee of null terminator). + return new string(reversed, 0, len); + } + */ + internal static string Reverse(this string input) + { + char[] chars = input.ToCharArray(); + Array.Reverse(chars); + return new string(chars); + } + } +} diff --git a/WakeOnLan/Topology/UIntExtensions.cs b/WakeOnLan/Topology/UIntExtensions.cs new file mode 100644 index 0000000..ccad88d --- /dev/null +++ b/WakeOnLan/Topology/UIntExtensions.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace System.Net.Topology +{ + internal static class UIntExtensions + { + // TODO: Tests + public static int CountOnesFromLeft(this uint value) + { + var occurences = 0; + for (uint i = 1u << 31; i >= 0; i >>= 1) + { + if ((i & value) != 0) + ++occurences; + else + break; + } + return occurences; + } + + // TODO: Tests + public static uint CreateWithOnesFromLeft(int count) + { + if (count > 32) + throw new ArgumentException("Cannot set more than 32 bits to one!"); + + var value = 0u; + + var mask = 1u << 31; + for (int i = 0; i < count; ++i) + { + value |= mask; + mask >>= 1; + } + + return value; + } + } +} diff --git a/WakeOnLan/WakeOnLan.csproj b/WakeOnLan/WakeOnLan.csproj new file mode 100644 index 0000000..b9ef27c --- /dev/null +++ b/WakeOnLan/WakeOnLan.csproj @@ -0,0 +1,26 @@ + + + + netstandard2.0 + Niklas Mollenhauer + 2.0.2 + Library for Sending magic packets and performing IP address operations. + LGPL-3.0-only + https://github.com/nikeee/wake-on-lan + https://github.com/nikeee/wake-on-lan.git + WakeOnLan, magic packets + True + System.Net + + + + latest + + + + + + + + +