337 lines
12 KiB
C#
337 lines
12 KiB
C#
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/*
|
|
* Copyright 2010 Evan Plaice <evanplaice@gmail.com>
|
|
* Copyright 2010 Chris Morgan <chmorgan@gmail.com>
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net.NetworkInformation;
|
|
using System.Text;
|
|
using PacketDotNet.Utils;
|
|
|
|
namespace PacketDotNet
|
|
{
|
|
/// <summary>
|
|
/// Wake-On-Lan
|
|
/// See: http://en.wikipedia.org/wiki/Wake-on-LAN
|
|
/// See: http://wiki.wireshark.org/WakeOnLAN
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a Wake-On-LAN packet from the destination MAC address
|
|
/// </summary>
|
|
/// <param name="destinationMAC">
|
|
/// A <see cref="System.Net.NetworkInformation.PhysicalAddress"/>
|
|
/// </param>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="bas">
|
|
/// A <see cref="ByteArraySegment"/>
|
|
/// </param>
|
|
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
|
|
|
|
/// <summary>
|
|
/// The Physical Address (MAC) of the host being woken up from sleep
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Returns the embedded Wake-On-LAN packet
|
|
/// or null if there is none
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A Wake-On-LAN packet
|
|
/// </returns>
|
|
[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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate a random WakeOnLanPacket
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A <see cref="WakeOnLanPacket"/>
|
|
/// </returns>
|
|
public static WakeOnLanPacket RandomPacket()
|
|
{
|
|
var rnd = new Random();
|
|
|
|
byte[] destAddress = new byte[EthernetFields.MacAddressLength];
|
|
|
|
rnd.NextBytes(destAddress);
|
|
|
|
return new WakeOnLanPacket(new PhysicalAddress(destAddress));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
/// <returns>
|
|
/// True if the Wake-On-LAN payload is valid
|
|
/// </returns>
|
|
public bool IsValid()
|
|
{
|
|
return IsValid(header);
|
|
}
|
|
|
|
/// <summary>
|
|
/// See IsValid
|
|
/// </summary>
|
|
/// <param name="bas">
|
|
/// A <see cref="ByteArraySegment"/>
|
|
/// </param>
|
|
/// <returns>
|
|
/// A <see cref="System.Boolean"/>
|
|
/// </returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compare two instances
|
|
/// </summary>
|
|
/// <param name="obj">
|
|
/// A <see cref="System.Object"/>
|
|
/// </param>
|
|
/// <returns>
|
|
/// A <see cref="System.Boolean"/>
|
|
/// </returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// GetHashCode override
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A <see cref="System.Int32"/>
|
|
/// </returns>
|
|
public override int GetHashCode()
|
|
{
|
|
return header.GetHashCode();
|
|
}
|
|
|
|
/// <summary cref="Packet.ToString(StringOutputType)" />
|
|
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<string,string> properties = new Dictionary<string,string>();
|
|
properties.Add("destination", HexPrinter.PrintMACAddress(DestinationMAC));
|
|
|
|
// calculate the padding needed to right-justify the property names
|
|
int padLength = Utils.RandomUtils.LongestStringLength(new List<string>(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
|
|
}
|
|
}
|