/* * Copyright (c) 2006, the libsecondlife development team * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the Second Life Reverse Engineering Team nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Xml; using System.Xml.Serialization; using libsecondlife; namespace libsecondlife.Packets { /// /// Thrown when a packet could not be successfully deserialized /// public class MalformedDataException : ApplicationException { /// /// Default constructor /// public MalformedDataException() { } /// /// Constructor that takes an additional error message /// /// An error message to attach to this exception public MalformedDataException(string Message) : base(Message) { this.Source = "Packet decoding"; } } /// /// The Second Life header of a message template packet. Either 5, 6, or 8 /// bytes in length at the beginning of the packet, and encapsulates any /// appended ACKs at the end of the packet as well /// #if PACKETSERIALIZE [XmlInclude(typeof(LowHeader))] [XmlInclude(typeof(MediumHeader))] [XmlInclude(typeof(HighHeader))] #endif public abstract class Header { /// Raw header data, does not include appended ACKs public byte[] Data; /// Raw value of the flags byte [XmlIgnore] public byte Flags { get { return Data[0]; } set { Data[0] = value; } } /// Reliable flag, whether this packet requires an ACK [XmlIgnore] public bool Reliable { get { return (Data[0] & Helpers.MSG_RELIABLE) != 0; } set { if (value) { Data[0] |= (byte)Helpers.MSG_RELIABLE; } else { byte mask = (byte)Helpers.MSG_RELIABLE ^ 0xFF; Data[0] &= mask; } } } /// Resent flag, whether this same packet has already been /// sent [XmlIgnore] public bool Resent { get { return (Data[0] & Helpers.MSG_RESENT) != 0; } set { if (value) { Data[0] |= (byte)Helpers.MSG_RESENT; } else { byte mask = (byte)Helpers.MSG_RESENT ^ 0xFF; Data[0] &= mask; } } } /// Zerocoded flag, whether this packet is compressed with /// zerocoding [XmlIgnore] public bool Zerocoded { get { return (Data[0] & Helpers.MSG_ZEROCODED) != 0; } set { if (value) { Data[0] |= (byte)Helpers.MSG_ZEROCODED; } else { byte mask = (byte)Helpers.MSG_ZEROCODED ^ 0xFF; Data[0] &= mask; } } } /// Appended ACKs flag, whether this packet has ACKs appended /// to the end [XmlIgnore] public bool AppendedAcks { get { return (Data[0] & Helpers.MSG_APPENDED_ACKS) != 0; } set { if (value) { Data[0] |= (byte)Helpers.MSG_APPENDED_ACKS; } else { byte mask = (byte)Helpers.MSG_APPENDED_ACKS ^ 0xFF; Data[0] &= mask; } } } /// Packet sequence number, three bytes long [XmlIgnore] public uint Sequence { get { return (uint)((Data[1] << 16) + (Data[2] << 8) + Data[3]); } set { Data[1] = (byte)(value >> 16); Data[2] = (byte)(value >> 8); Data[3] = (byte)(value % 256); } } /// Numeric ID number of this packet [XmlIgnore] public abstract ushort ID { get; set; } /// Frequency classification of this packet, Low Medium or /// High [XmlIgnore] public abstract PacketFrequency Frequency { get; } /// Convert this header to a byte array, not including any /// appended ACKs public abstract void ToBytes(byte[] bytes, ref int i); /// Array containing all the appended ACKs of this packet public uint[] AckList; /// /// Convert the AckList to a byte array, used for packet serializing /// /// Reference to the target byte array /// Beginning position to start writing to in the byte /// array, will be updated with the ending position of the ACK list public void AcksToBytes(byte[] bytes, ref int i) { foreach (uint ack in AckList) { bytes[i++] = (byte)((ack >> 24) % 256); bytes[i++] = (byte)((ack >> 16) % 256); bytes[i++] = (byte)((ack >> 8) % 256); bytes[i++] = (byte)(ack % 256); } if (AckList.Length > 0) { bytes[i++] = (byte)AckList.Length; } } /// /// /// /// /// /// /// public static Header BuildHeader(byte[] bytes, ref int pos, ref int packetEnd) { if (bytes[4] == 0xFF) { if (bytes[5] == 0xFF) { return new LowHeader(bytes, ref pos, ref packetEnd); } else { return new MediumHeader(bytes, ref pos, ref packetEnd); } } else { return new HighHeader(bytes, ref pos, ref packetEnd); } } /// /// /// /// /// protected void CreateAckList(byte[] bytes, ref int packetEnd) { if (AppendedAcks) { try { int count = bytes[packetEnd--]; AckList = new uint[count]; for (int i = 0; i < count; i++) { AckList[i] = (uint)( (bytes[(packetEnd - i * 4) - 3] << 24) | (bytes[(packetEnd - i * 4) - 2] << 16) | (bytes[(packetEnd - i * 4) - 1] << 8) | (bytes[(packetEnd - i * 4) ])); } packetEnd -= (count * 4); } catch (Exception) { AckList = new uint[0]; throw new MalformedDataException(); } } else { AckList = new uint[0]; } } } /// /// /// public class LowHeader : Header { /// public override ushort ID { get { return (ushort)((Data[6] << 8) + Data[7]); } set { Data[6] = (byte)(value >> 8); Data[7] = (byte)(value % 256); } } /// public override PacketFrequency Frequency { get { return PacketFrequency.Low; } } /// /// /// public LowHeader() { Data = new byte[8]; Data[4] = Data[5] = 0xFF; AckList = new uint[0]; } /// /// /// /// /// /// public LowHeader(byte[] bytes, ref int pos, ref int packetEnd) { if (bytes.Length < 8) { throw new MalformedDataException(); } Data = new byte[8]; Array.Copy(bytes, Data, 8); if ((bytes[0] & Helpers.MSG_ZEROCODED) != 0 && bytes[6] == 0) { if (bytes[7] == 1) { Data[7] = bytes[8]; } else { throw new MalformedDataException(); } } pos = 8; CreateAckList(bytes, ref packetEnd); } /// /// /// /// /// public override void ToBytes(byte[] bytes, ref int i) { Array.Copy(Data, 0, bytes, i, 8); i += 8; } } /// /// /// public class MediumHeader : Header { /// public override ushort ID { get { return (ushort)Data[5]; } set { Data[5] = (byte)value; } } /// public override PacketFrequency Frequency { get { return PacketFrequency.Medium; } } /// /// /// public MediumHeader() { Data = new byte[6]; Data[4] = 0xFF; AckList = new uint[0]; } /// /// /// /// /// /// public MediumHeader(byte[] bytes, ref int pos, ref int packetEnd) { if (bytes.Length < 6) { throw new MalformedDataException(); } Data = new byte[6]; Array.Copy(bytes, Data, 6); pos = 6; CreateAckList(bytes, ref packetEnd); } /// /// /// /// /// public override void ToBytes(byte[] bytes, ref int i) { Array.Copy(Data, 0, bytes, i, 6); i += 6; } } /// /// /// public class HighHeader : Header { /// public override ushort ID { get { return (ushort)Data[4]; } set { Data[4] = (byte)value; } } /// public override PacketFrequency Frequency { get { return PacketFrequency.High; } } /// /// /// public HighHeader() { Data = new byte[5]; AckList = new uint[0]; } /// /// /// /// /// /// public HighHeader(byte[] bytes, ref int pos, ref int packetEnd) { if (bytes.Length < 5) { throw new MalformedDataException(); } Data = new byte[5]; Array.Copy(bytes, Data, 5); pos = 5; CreateAckList(bytes, ref packetEnd); } /// /// /// /// /// public override void ToBytes(byte[] bytes, ref int i) { Array.Copy(Data, 0, bytes, i, 5); i += 5; } }