/* This file is part of PacketDotNet PacketDotNet is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. PacketDotNet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PacketDotNet. If not, see . */ /* * Copyright 2010 Evan Plaice * Copyright 2010 Chris Morgan */ using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; using System.Text; using PacketDotNet.Utils; namespace PacketDotNet { /// /// Wake-On-Lan /// See: http://en.wikipedia.org/wiki/Wake-on-LAN /// See: http://wiki.wireshark.org/WakeOnLAN /// public class WakeOnLanPacket : Packet { // NOTE: No need to warn about lack of use, the compiler won't // put any calls to 'log' here but we need 'log' to exist to compile #pragma warning disable 0169, 0649 private static readonly ILogInactive log; #pragma warning restore 0169, 0649 #region Constructors static WakeOnLanPacket() { packetLength = macRepetitions * EthernetFields.MacAddressLength + syncSequence.Length; } /// /// Create a Wake-On-LAN packet from the destination MAC address /// /// /// A /// public WakeOnLanPacket(PhysicalAddress destinationMAC) { log.Debug(""); // allocate memory for this packet int offset = 0; var packetBytes = new byte[packetLength]; var destinationMACBytes = destinationMAC.GetAddressBytes(); // write the data to the payload // - synchronization sequence (6 bytes) // - destination MAC (16 copies of 6 bytes) for(int i = 0; i < packetLength; i+=EthernetFields.MacAddressLength) { // copy the syncSequence on the first pass if(i == 0) { Array.Copy(syncSequence, 0, packetBytes, i, syncSequence.Length); } else { Array.Copy(destinationMACBytes, 0, packetBytes, i, EthernetFields.MacAddressLength); } } header = new ByteArraySegment(packetBytes, offset, packetLength); } /// /// Constructor /// /// /// A /// public WakeOnLanPacket(ByteArraySegment bas) { log.Debug(""); RandomUtils.EnsurePacketLength(this, packetLength, bas.Length); if(WakeOnLanPacket.IsValid(bas)) { // set the header field, header field values are retrieved from this byte array header = new ByteArraySegment(bas); header.Length = Bytes.Length; } } #endregion #region Properties /// /// The Physical Address (MAC) of the host being woken up from sleep /// public PhysicalAddress DestinationMAC { get { byte[] destinationMAC = new byte[EthernetFields.MacAddressLength]; Array.Copy(header.Bytes, header.Offset + syncSequence.Length, destinationMAC, 0, EthernetFields.MacAddressLength); return new PhysicalAddress(destinationMAC); } set { byte[] destinationMAC = value.GetAddressBytes(); Array.Copy(destinationMAC, 0, header.Bytes, header.Offset + syncSequence.Length, EthernetFields.MacAddressLength); } } #endregion #region Methods /// /// Returns the embedded Wake-On-LAN packet /// or null if there is none /// /// /// A Wake-On-LAN packet /// [Obsolete("Use Packet.Extract() instead")] public static WakeOnLanPacket GetEncapsulated(Packet p) { // see if we have an ethernet packet that contains a wol packet var ethernetPacket = (EthernetPacket)p.Extract(typeof(EthernetPacket)); if(ethernetPacket != null) { if(ethernetPacket.Type == EthernetPacketType.WakeOnLan) { return (WakeOnLanPacket)ethernetPacket.PayloadPacket; } } // otherwise see if we have a udp packet (might have been sent to port 7 or 9) that // contains a wol packet var udpPacket = (UdpPacket)p.Extract(typeof(UdpPacket)); if(udpPacket != null) { // if the destination port is 7 or 9 then this is already parsed as a // WakeOnLan packet so just return it if((udpPacket.DestinationPort == 7) || (udpPacket.DestinationPort == 9)) { return (WakeOnLanPacket)udpPacket.PayloadPacket; } } return null; } /// /// Generate a random WakeOnLanPacket /// /// /// A /// public static WakeOnLanPacket RandomPacket() { var rnd = new Random(); byte[] destAddress = new byte[EthernetFields.MacAddressLength]; rnd.NextBytes(destAddress); return new WakeOnLanPacket(new PhysicalAddress(destAddress)); } /// /// Checks the validity of the Wake-On-LAN payload /// - by checking the synchronization sequence /// - by checking to see if there are 16 iterations of the Destination MAC address /// /// /// True if the Wake-On-LAN payload is valid /// public bool IsValid() { return IsValid(header); } /// /// See IsValid /// /// /// A /// /// /// A /// public static bool IsValid(ByteArraySegment bas) { // fetch the destination MAC from the payload byte[] destinationMAC = new byte[EthernetFields.MacAddressLength]; Array.Copy(bas.Bytes, bas.Offset + syncSequence.Length, destinationMAC, 0, EthernetFields.MacAddressLength); // the buffer is used to store both the synchronization sequence // and the MAC address, both of which are the same length (in bytes) byte[] buffer = new byte[EthernetFields.MacAddressLength]; // validate the 16 repetitions of the wolDestinationMAC // - verify that the wolDestinationMAC address repeats 16 times in sequence for(int i = 0; i<(EthernetFields.MacAddressLength * macRepetitions); i+=EthernetFields.MacAddressLength) { // Extract the sample from the payload for comparison Array.Copy(bas.Bytes, bas.Offset + i, buffer, 0, buffer.Length); // check the synchronization sequence on the first pass if(i == 0) { // validate the synchronization sequence if(!buffer.SequenceEqual(syncSequence)) return false; } else { // fail the validation on malformed WOL Magic Packets if(!buffer.SequenceEqual(destinationMAC)) return false; } } return true; } /// /// Compare two instances /// /// /// A /// /// /// A /// public override bool Equals(object obj) { // Check for null values and compare run-time types. if (obj == null || GetType() != obj.GetType()) return false; var wol = (WakeOnLanPacket)obj; return DestinationMAC.Equals(wol.DestinationMAC); } /// /// GetHashCode override /// /// /// A /// public override int GetHashCode() { return header.GetHashCode(); } /// public override string ToString(StringOutputType outputFormat) { var buffer = new StringBuilder(); string color = ""; string colorEscape = ""; if(outputFormat == StringOutputType.Colored || outputFormat == StringOutputType.VerboseColored) { color = Color; colorEscape = AnsiEscapeSequences.Reset; } if(outputFormat == StringOutputType.Normal || outputFormat == StringOutputType.Colored) { buffer.AppendFormat("[{0}WakeOnLanPacket{1}: DestinationMAC={2}]", color, colorEscape, DestinationMAC); } if(outputFormat == StringOutputType.Verbose || outputFormat == StringOutputType.VerboseColored) { // collect the properties and their value Dictionary properties = new Dictionary(); properties.Add("destination", HexPrinter.PrintMACAddress(DestinationMAC)); // calculate the padding needed to right-justify the property names int padLength = Utils.RandomUtils.LongestStringLength(new List(properties.Keys)); // build the output string buffer.AppendLine("WOL: ******* WOL - \"Wake-On-Lan\" - offset=? length=" + TotalPacketLength); buffer.AppendLine("WOL:"); foreach(var property in properties) { buffer.AppendLine("WOL: " + property.Key.PadLeft(padLength) + " = " + property.Value); } buffer.AppendLine("WOL:"); } // append the base string output buffer.Append(base.ToString(outputFormat)); return buffer.ToString(); } #endregion #region Members // the WOL synchronization sequence private static readonly byte[] syncSequence = new byte[6] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; // the number of times the Destination MAC appears in the payload private static readonly int macRepetitions = 16; private static readonly int packetLength; #endregion } }