using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Kneesocks { public class Frame { public enum kOpcode { Continuation = 0x0, TextFrame = 0x1, BinaryFrame = 0x2, Close = 0x8, Ping = 0x9, Pong = 0xA }; public kOpcode Opcode { get; set; } public bool IsFinal { get; set; } public bool IsMasked { get; set; } public byte[] Mask { get; set; } = new byte[] { 0, 0, 0, 0 }; public byte Reserved { get; set; } public byte[] Content { get; set; } public byte[] MaskedContent { get { byte[] returnValue = new byte[Content.Length]; for(long i = 0; i < Content.LongLength; ++i) returnValue[i] = (byte)(Content[i] ^ Mask[i % 4]); return returnValue; } } public static Frame FromRaw(byte[] raw) { if(raw.Length < 2) throw new FormatException("Websocket frame cannot be less than two bytes long"); var rawOpcode = raw[0] & 0x0F; if(!Enum.IsDefined(typeof(kOpcode), rawOpcode)) throw new FormatException("Opcode '"+ rawOpcode.ToString("0x{0:X}") +"' not understood"); var returnFrame = new Frame { IsFinal = (raw[0] & 0x80) != 0, Opcode = (kOpcode)rawOpcode, IsMasked = (raw[1] & 0x80) != 0, Reserved = (byte)((raw[0] & 0x70) >> 4) }; ulong frameLength = raw[1] & 0x7Ful; int lengthOffset = frameLength < 126 ? 1 : (frameLength == 126 ? 3 : 9); for(var i = lengthOffset - 1; i > 0; --i) { var bytePos = lengthOffset - 1 - i; frameLength &= 0xFFul << bytePos; frameLength |= (ulong)raw[2 + i] << bytePos; } if(returnFrame.IsMasked) { for(var i = 0; i < 4; ++i) returnFrame.Mask[i] = raw[i + 1 + lengthOffset]; lengthOffset += 4; } if(raw.LongLength + lengthOffset + 1 < frameLength) throw new FormatException("Raw frame length passed in is undersized ") return returnFrame; } } }