2017-04-24 21:04:52 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
2017-04-27 20:44:30 +00:00
|
|
|
|
namespace Kneesocks {
|
|
|
|
|
public class Frame {
|
2017-04-25 21:01:41 +00:00
|
|
|
|
public enum kOpcode {
|
|
|
|
|
Continuation = 0x0,
|
|
|
|
|
TextFrame = 0x1,
|
|
|
|
|
BinaryFrame = 0x2,
|
|
|
|
|
Close = 0x8,
|
|
|
|
|
Ping = 0x9,
|
|
|
|
|
Pong = 0xA
|
|
|
|
|
};
|
|
|
|
|
|
2017-04-27 20:44:57 +00:00
|
|
|
|
public kOpcode Opcode { get; set; }
|
|
|
|
|
public bool IsFinal { get; set; }
|
|
|
|
|
public bool IsMasked { get; set; }
|
2017-04-29 13:06:20 +00:00
|
|
|
|
public byte[] Mask { get; set; } = new byte[] { 0, 0, 0, 0 };
|
2017-04-27 20:44:57 +00:00
|
|
|
|
public byte Reserved { get; set; }
|
2017-04-28 21:01:58 +00:00
|
|
|
|
|
|
|
|
|
public byte[] Content { get; set; }
|
|
|
|
|
public byte[] MaskedContent {
|
|
|
|
|
get {
|
2017-04-29 13:06:20 +00:00
|
|
|
|
byte[] returnValue = new byte[Content.Length];
|
2017-04-29 21:07:17 +00:00
|
|
|
|
for(long i = 0; i < Content.LongLength; ++i)
|
2017-04-29 13:06:20 +00:00
|
|
|
|
returnValue[i] = (byte)(Content[i] ^ Mask[i % 4]);
|
|
|
|
|
return returnValue;
|
2017-04-28 21:01:58 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-24 21:04:52 +00:00
|
|
|
|
|
2017-05-01 12:18:27 +00:00
|
|
|
|
public byte[] GetRaw() {
|
|
|
|
|
var headerSize = 2L;
|
|
|
|
|
var bodySize = Content.LongLength;
|
|
|
|
|
if(bodySize >= 0x7E && bodySize <= 0xFFFF)
|
|
|
|
|
headerSize += 2;
|
|
|
|
|
else if(bodySize > 0xFFFF)
|
|
|
|
|
headerSize += 8;
|
|
|
|
|
if(IsMasked)
|
|
|
|
|
headerSize += 4;
|
|
|
|
|
|
|
|
|
|
var returnValue = new byte[headerSize + bodySize];
|
|
|
|
|
|
|
|
|
|
return returnValue;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-29 13:06:20 +00:00
|
|
|
|
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)
|
|
|
|
|
};
|
|
|
|
|
|
2017-05-01 12:18:27 +00:00
|
|
|
|
ulong bodyLength = raw[1] & 0x7Ful;
|
2017-04-29 13:06:20 +00:00
|
|
|
|
int lengthOffset =
|
2017-05-01 12:18:27 +00:00
|
|
|
|
bodyLength < 126
|
2017-04-29 13:06:20 +00:00
|
|
|
|
? 1
|
2017-05-01 12:18:27 +00:00
|
|
|
|
: (bodyLength == 126 ? 3 : 9);
|
2017-04-29 21:07:17 +00:00
|
|
|
|
|
|
|
|
|
for(var i = lengthOffset - 1; i > 0; --i) {
|
|
|
|
|
var bytePos = lengthOffset - 1 - i;
|
2017-05-01 12:18:27 +00:00
|
|
|
|
bodyLength &= 0xFFul << bytePos;
|
|
|
|
|
bodyLength |= (ulong)raw[2 + i] << bytePos;
|
2017-04-29 21:07:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(returnFrame.IsMasked) {
|
|
|
|
|
for(var i = 0; i < 4; ++i)
|
|
|
|
|
returnFrame.Mask[i] = raw[i + 1 + lengthOffset];
|
|
|
|
|
lengthOffset += 4;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-01 12:18:27 +00:00
|
|
|
|
ulong expectedFrameLength = bodyLength + (uint)lengthOffset + 1;
|
|
|
|
|
if(expectedFrameLength < (ulong)raw.LongLength)
|
|
|
|
|
throw new FormatException("Raw frame length ("+ expectedFrameLength +") is less than described size ("+ (ulong)raw.LongLength +")");
|
|
|
|
|
|
|
|
|
|
ulong counter = 0;
|
|
|
|
|
returnFrame.Content = raw.Skip(lengthOffset + 1)
|
|
|
|
|
.TakeWhile(x => counter++ < bodyLength)
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
if(returnFrame.IsMasked) {
|
|
|
|
|
counter = 0;
|
|
|
|
|
returnFrame.Content =
|
|
|
|
|
returnFrame.Content.Select(
|
|
|
|
|
x => (byte)(x ^ returnFrame.Mask[counter++ % 4])
|
|
|
|
|
).ToArray();
|
|
|
|
|
}
|
2017-04-29 13:06:20 +00:00
|
|
|
|
|
|
|
|
|
return returnFrame;
|
2017-04-28 21:01:58 +00:00
|
|
|
|
}
|
2017-04-24 21:04:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|