#region Header /* 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 2011 David Thedens */ #endregion Header using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Text; using MiscUtil.Conversion; using PacketDotNet.Utils; using System.Linq; namespace PacketDotNet { namespace Ieee80211 { /// /// .Net analog of a PpiHeader.h from airpcap /// public class PpiPacket : InternetLinkLayerPacket, IEnumerable { /// /// PPI packet header flags. /// [Flags] public enum HeaderFlags : byte { /// /// Indicates whether or not the PPI fields are aligned to a 32 bit boundary. /// Alignment32Bit = 1 } #region Properties /// /// Length of the whole header in bytes /// public UInt16 Length { get { var length = PpiHeaderFields.PpiPacketHeaderLength; foreach (var field in PpiFields ) { length += PpiHeaderFields.FieldHeaderLength + field.Length; if((Flags & HeaderFlags.Alignment32Bit) == HeaderFlags.Alignment32Bit) { length += GetDistanceTo32BitAlignment(field.Length); } } return (UInt16)length; } } private UInt16 LengthBytes { get { return EndianBitConverter.Little.ToUInt16 (header.Bytes, header.Offset + PpiHeaderFields.LengthPosition); } set { EndianBitConverter.Little.CopyBytes (value, header.Bytes, header.Offset + PpiHeaderFields.LengthPosition); } } /// /// Version 0. Only increases for drastic changes, introduction of compatible /// new fields does not count. /// public byte Version { get; set; } private byte VersionBytes { get { return header.Bytes [header.Offset + PpiHeaderFields.VersionPosition]; } set { header.Bytes [header.Offset + PpiHeaderFields.VersionPosition] = value; } } /// /// Gets or sets the PPI header flags. /// /// /// The PPI header flags. /// public HeaderFlags Flags { get; set; } private HeaderFlags FlagsBytes { get { return (HeaderFlags)header.Bytes[header.Offset + PpiHeaderFields.FlagsPosition]; } set { header.Bytes[header.Offset + PpiHeaderFields.FlagsPosition] = (byte) value; } } /// /// Gets or sets the type of the link type specified in the PPI packet. This should /// be the link type of the encapsulated packet. /// /// /// The link type. /// public LinkLayers LinkType { get; set; } private LinkLayers LinkTypeBytes { get { return (LinkLayers) EndianBitConverter.Little.ToUInt32(header.Bytes, header.Offset + PpiHeaderFields.DataLinkTypePosition); } set { EndianBitConverter.Little.CopyBytes((uint)LinkType, header.Bytes, header.Offset + PpiHeaderFields.DataLinkTypePosition); } } /// /// Returns the number of PPI fields in the PPI packet. /// /// /// The number of fields. /// public int Count { get { return PpiFields.Count; } } /// /// Gets the at the specified index. /// /// /// Index. /// public PpiField this[int index] { get { return PpiFields[index]; } } private List PpiFields { get; set; } #endregion Properties #region Constructors /// /// Initializes a new instance of the class. /// /// /// A /// public PpiPacket (ByteArraySegment bas) { // slice off the header portion header = new ByteArraySegment (bas); Version = VersionBytes; Flags = FlagsBytes; // update the header size based on the headers packet length header.Length = LengthBytes; LinkType = LinkTypeBytes; PpiFields = ReadPpiFields(); PpiCommon commonField = FindFirstByType(PpiFieldType.PpiCommon) as PpiCommon; // parse the encapsulated bytes payloadPacketOrData = ParseEncapsulatedBytes (header, commonField); } /// /// Initializes a new instance of the class. /// public PpiPacket () { PpiFields = new List(); Version = 0; LinkType = LinkLayers.Ieee80211; } #endregion Constructors #region Public Methods /// /// Add the specified field to the packet. /// /// /// the field. /// public void Add(PpiField field) { PpiFields.Add(field); } /// /// Removes the specified field from the packet. /// /// /// the field. /// public void Remove(PpiField field) { PpiFields.Remove(field); } /// /// Removes all fields of the specified type. /// /// /// the field type to be removed. /// public void RemoveAll(PpiFieldType type) { PpiFields.RemoveAll( field => type == field.FieldType); } /// /// Checks whether the specified field is in the packet. /// /// /// true if the field is in the packet, false if not. /// public bool Contains(PpiField field) { return PpiFields.Contains(field); } /// /// Checks whether there is field of the specified type in the packet. /// /// /// true if there is a field of the specified type in the packet, false if not. /// public bool Contains(PpiFieldType type) { return (PpiFields.Find(field => field.FieldType == type) != null); } /// /// Finds the first field in the packet of the specified type. /// /// /// The first field in the packet of the specified type, or null if there is no field of the specified type. /// /// /// The type of packet to find. /// public PpiField FindFirstByType (PpiFieldType type) { var ppiFields = this.PpiFields; foreach (var r in ppiFields) { if (r.FieldType == type) return r; } return null; } /// /// Finds the fields in the packet of the specified type. /// /// /// The fields of the specified type, or an empty array of there are no fields of that type. /// /// /// The type of packet to find. /// public PpiField[] FindByType(PpiFieldType type) { return PpiFields.FindAll(p => (p.FieldType == type)).ToArray(); } /// /// Returns the Ieee80211MacFrame inside of the Packet p or null if /// there is no encapsulated packet /// /// /// A /// /// /// A /// [Obsolete("Use Packet.Extract() instead")] public static MacFrame GetEncapsulated (Packet p) { if (p is PpiPacket) { var payload = p.PayloadPacket; return (MacFrame)payload; } return null; } /// 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) { // build the output string buffer.AppendFormat ("{0}[Ieee80211PpiPacket: Version={2}, Length={3}, {1}", color, colorEscape, Version, Length ); } if (outputFormat == StringOutputType.Verbose || outputFormat == StringOutputType.VerboseColored) { // collect the properties and their value var properties = new Dictionary (); properties.Add ("version", Version.ToString ()); properties.Add ("length", Length.ToString ()); var ppiField = this.PpiFields; foreach (var r in ppiField) { properties.Add (r.FieldType.ToString (), r.ToString ()); } // calculate the padding needed to right-justify the property names int padLength = RandomUtils.LongestStringLength (new List (properties.Keys)); // build the output string buffer.AppendLine ("Ieee80211PpiPacket"); foreach (var property in properties) { buffer.AppendLine ("PPI: " + property.Key.PadLeft (padLength) + " = " + property.Value); } buffer.AppendLine ("PPI:"); } // append the base output buffer.Append (base.ToString (outputFormat)); return buffer.ToString (); } /// /// Gets the enumerator of PPI fields. /// /// /// The field enumerator. /// public IEnumerator GetEnumerator() { return PpiFields.GetEnumerator(); } /// /// Called to ensure that field values are updated before /// the packet bytes are retrieved /// public override void UpdateCalculatedValues() { //If aligned is true then fields must all start on 32bit boundaries so we might need //to read some extra padding from the end of the header fields. bool aligned = ((Flags & HeaderFlags.Alignment32Bit) == HeaderFlags.Alignment32Bit); var totalFieldLength = Length; if ((header == null) || (totalFieldLength > header.Length)) { header = new ByteArraySegment (new Byte[totalFieldLength]); } header.Length = totalFieldLength; VersionBytes = Version; FlagsBytes = Flags; LengthBytes = (ushort)totalFieldLength; LinkTypeBytes = LinkType; MemoryStream ms = new MemoryStream(header.Bytes, header.Offset + PpiHeaderFields.FirstFieldPosition, totalFieldLength - PpiHeaderFields.FirstFieldPosition); BinaryWriter writer = new BinaryWriter(ms); foreach (var field in PpiFields) { writer.Write((ushort) field.FieldType); writer.Write((ushort) field.Length); writer.Write(field.Bytes); var paddingBytesRequired = GetDistanceTo32BitAlignment(field.Length); if(aligned && (paddingBytesRequired > 0)) { writer.Write(new byte[paddingBytesRequired]); } } } #endregion Public Methods #region Private Methods /// /// Array of PPI fields /// private List ReadPpiFields() { //If aligned is true then fields must all start on 32bit boundaries so we might need //to read some extra padding from the end of the header fields. bool aligned = ((Flags & HeaderFlags.Alignment32Bit) == HeaderFlags.Alignment32Bit); var retList = new List (); // create a binary reader that points to the memory immediately after the dtl var offset = header.Offset + PpiHeaderFields.FirstFieldPosition; var br = new BinaryReader (new MemoryStream (header.Bytes, offset, (int)(header.Length - offset))); int type = 0; int length = PpiHeaderFields.FirstFieldPosition; while(length < header.Length) { type = br.ReadUInt16 (); var fieldLength = br.ReadUInt16 (); //add the length plus 4 for the type and length fields length += fieldLength + 4; retList.Add (PpiField.Parse (type, br, fieldLength)); var paddingByteCount = GetDistanceTo32BitAlignment(fieldLength); if(aligned && (paddingByteCount > 0)) { br.ReadBytes(paddingByteCount); length += paddingByteCount; } } return retList; } /// /// Used by the Ieee80211PpiPacket constructor. /// /// /// A /// /// /// The object in the PPI packet or null if not available /// /// /// A /// internal static PacketOrByteArraySegment ParseEncapsulatedBytes (ByteArraySegment header, PpiCommon commonField) { // slice off the payload var payload = header.EncapsulatedBytes (); var payloadPacketOrData = new PacketOrByteArraySegment (); MacFrame frame = null; if (commonField != null) { bool fcsPresent = ((commonField.Flags & PpiCommon.CommonFlags.FcsIncludedInFrame) == PpiCommon.CommonFlags.FcsIncludedInFrame); if (fcsPresent) { frame = MacFrame.ParsePacketWithFcs (payload); } else { frame = MacFrame.ParsePacket (payload); } } else { frame = MacFrame.ParsePacket (payload); } if (frame == null) { payloadPacketOrData.TheByteArraySegment = payload; } else { payloadPacketOrData.ThePacket = frame; } return payloadPacketOrData; } private int GetDistanceTo32BitAlignment(int length) { return ((length % 4) == 0) ? 0 : 4 - (length % 4); } #endregion Private Methods } } }