using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using Ionic.BZip2; using System.Net; using Ionic.Crc; namespace QueryMaster { internal class UdpQuery : ServerSocket { private const int SinglePacket = -1; private const int MultiPacket = -2; private EngineType Type; internal UdpQuery(IPEndPoint address, int sendTimeOut, int receiveTimeOut) : base(SocketType.Udp) { Connect(address); socket.SendTimeout = sendTimeOut; socket.ReceiveTimeout = receiveTimeOut; } internal byte[] GetResponse(byte[] msg, EngineType type) { Type = type; byte[] recvData; SendData(msg); recvData = ReceiveData(); try { int header = BitConverter.ToInt32(recvData, 0); switch (header) { case SinglePacket: return ParseSinglePkt(recvData); case MultiPacket: return ParseMultiPkt(recvData); default: throw new InvalidHeaderException("Protocol header is not valid"); } } catch (Exception e) { e.Data.Add("ReceivedData", recvData); throw; } } private byte[] ParseSinglePkt(byte[] data) { return data.Skip(4).ToArray(); } private byte[] ParseMultiPkt(byte[] data) { switch (Type) { case EngineType.Source: return SourcePackets(data); case EngineType.GoldSource: return GoldSourcePackets(data); default: throw new ArgumentException("An invalid EngineType was specified."); } } private byte[] GoldSourcePackets(byte[] data) { var pktCount = data[8] & 0x0F; List> pktList = new List>(pktCount); pktList.Add(new KeyValuePair(data[8] >> 4, data)); byte[] recvData; for (int i = 1; i < pktCount; i++) { recvData = new byte[BufferSize]; recvData = ReceiveData(); pktList.Add(new KeyValuePair(recvData[8] >> 4, recvData)); } pktList.Sort((x, y) => x.Key.CompareTo(y.Key)); List byteList = new List(); byteList.AddRange(pktList[0].Value.Skip(13)); for (int i = 1; i < pktList.Count; i++) { byteList.AddRange(pktList[i].Value.Skip(9)); } return byteList.ToArray(); } private byte[] SourcePackets(byte[] data) { byte pktCount = data[8]; List> pktList = new List>(pktCount); pktList.Add(new KeyValuePair(data[9], data)); byte[] recvData; for (int i = 1; i < pktCount; i++) { recvData = ReceiveData(); pktList.Add(new KeyValuePair(recvData[9], recvData)); } pktList.Sort((x, y) => x.Key.CompareTo(y.Key)); Parser parser = null; bool isCompressed = false; int checksum = 0; List recvList = new List(); parser = new Parser(pktList[0].Value); parser.Skip(4);//header if (parser.ReadInt() < 0)//ID isCompressed = true; parser.ReadByte();//total int pktId = parser.ReadByte();// packet id parser.ReadShort();//size if (isCompressed) { parser.Skip(2);//[this is not equal to decompressed length of data] checksum = parser.ReadInt();//Checksum } recvList.AddRange(parser.GetUnParsedData()); for (int i = 1; i < pktList.Count; i++) { parser = new Parser(pktList[i].Value); parser.Skip(12);//multipacket header only recvList.AddRange(parser.GetUnParsedData()); } recvData = recvList.ToArray(); if (isCompressed) { recvData = Decompress(recvData); if (!IsValid(recvData, checksum)) throw new InvalidPacketException("packet's checksum value does not match with the calculated checksum"); } return recvData.Skip(4).ToArray(); } private byte[] Decompress(byte[] data) { using (var input = new MemoryStream(data)) using (var output = new MemoryStream()) using (var unZip = new BZip2InputStream(input)) { int ch = unZip.ReadByte(); while (ch != -1) { output.WriteByte((byte)ch); ch = unZip.ReadByte(); } output.Flush(); return output.ToArray(); } } private bool IsValid(byte[] data, int Checksum) { using (var Input = new MemoryStream(data)) { if (Checksum == new CRC32().GetCrc32(Input)) return true; return false; } } } }