diff --git a/server/Kneesocks/Connection.cs b/server/Kneesocks/Connection.cs
index 365b651..81a56e0 100644
--- a/server/Kneesocks/Connection.cs
+++ b/server/Kneesocks/Connection.cs
@@ -23,7 +23,9 @@ namespace Kneesocks {
private TcpClient Socket;
private NetworkStream Stream;
- private string RawBuffer = "";
+
+ ReadBuffer Buffer;
+ private Frame PartialFrame = null;
private List FrameBuffer = new List();
public bool Disconnected { get; private set; } = false;
@@ -38,16 +40,23 @@ namespace Kneesocks {
Socket = sock;
Socket.ReceiveTimeout = 1;
Stream = sock.GetStream();
+ Buffer = new ReadBuffer(Stream);
}
public Connection(UInt64 id, TcpClient sock) : this(sock) {
Id = id;
}
- public Connection(Connection conn) {
- Id = conn.Id;
+ public Connection(Connection conn, bool preserveId = true) {
+ if(preserveId)
+ _Id = conn._Id;
+
Socket = conn.Socket;
- Stream = Socket.GetStream();
+ Stream = conn.Stream;
+
+ Buffer = conn.Buffer;
+ PartialFrame = conn.PartialFrame;
+ FrameBuffer = conn.FrameBuffer;
Disconnected = conn.Disconnected;
DisconnectReason = conn.DisconnectReason;
@@ -57,11 +66,25 @@ namespace Kneesocks {
Headers = conn.Headers;
}
+ private void StartRead(ulong length) {
+
+ }
+
public byte[] Parse() {
+ byte[] readBuffer = null;
+ if(Buffer.IsReading) {
+ readBuffer = Buffer.AttemptRead();
+ if(readBuffer == null)
+ return null;
+ }
+
if(!Handshaked) {
+ if(Stream.)
+ return null;
} else {
-
+
+ OnParse();
}
}
@@ -85,16 +108,16 @@ namespace Kneesocks {
}
// called after the client successfully handshakes
- public virtual void OnOpen() { }
+ protected virtual void OnOpen() { }
// called when the thread manager iterates through
// the thread list and stops on this thread
- public virtual void OnParse() { }
+ protected virtual void OnParse() { }
// called when data has been received
- public virtual void OnReceive(byte[] data) { }
+ protected virtual void OnReceive(byte[] data) { }
// called when the connection is disconnected
- public virtual void OnClose() { }
+ protected virtual void OnClose() { }
}
}
diff --git a/server/Kneesocks/Frame.cs b/server/Kneesocks/Frame.cs
index ccc54ea..c05d97a 100644
--- a/server/Kneesocks/Frame.cs
+++ b/server/Kneesocks/Frame.cs
@@ -32,7 +32,29 @@ namespace Kneesocks {
public byte[] Mask { get; set; } = new byte[] { 0, 0, 0, 0 };
public byte Reserved { get; set; } = 0;
- public byte[] Content { get; set; }
+ private int _HeaderLength = 0;
+ public int HeaderLength {
+ get {
+ if(_HeaderLength != 0)
+ return _HeaderLength;
+
+ int length = 2
+ + (BodyLength >= 0x7E && BodyLength <= 0xFFFF ? 2 : 0)
+ + (BodyLength > 0xFFFF ? 4 : 0)
+ + (IsMasked ? 4 : 0);
+
+ return (_HeaderLength = length);
+ }
+ }
+
+ private int _BodyLength = 0;
+ public int BodyLength {
+ get {
+ return Content == null ? _BodyLength : Content.Length;
+ }
+ }
+
+ public byte[] Content { get; set; } = null;
public byte[] MaskedContent {
get {
long counter = 0;
@@ -41,8 +63,8 @@ namespace Kneesocks {
}
public byte[] GetBytes() {
- var headerSize = 2L;
- var bodySize = Content.LongLength;
+ var headerSize = 2;
+ var bodySize = (UInt64)Content.LongLength;
var headerLengthFirstByte = (byte)Content.Length;
if(bodySize >= 0x7E && bodySize <= 0xFFFF) {
headerSize += 2;
@@ -55,7 +77,7 @@ namespace Kneesocks {
if(IsMasked)
headerSize += 4;
- var returnValue = new byte[headerSize + bodySize];
+ var returnValue = new byte[(UInt64)headerSize + bodySize];
returnValue[0] = (byte)(((byte)Opcode % 0x10)
| ((Reserved % 8) << 4)
| (IsFinal ? 0x80 : 0x0));
@@ -76,44 +98,65 @@ namespace Kneesocks {
return returnValue;
}
- public static Frame FromBytes(byte[] raw) {
+ public static int HeaderSizeFromBytes(byte[] raw) {
+ if(raw.Length < 2)
+ throw new FormatException("Need first two bytes to analyze");
+
+ var lengthByte = raw[1] & 0x7F;
+ return 2
+ + ((raw[1] & 0x80) != 0 ? 4: 0)
+ + (lengthByte == 0x7E ? 2 : 0)
+ + (lengthByte == 0x7F ? 4 : 0);
+ }
+
+ public static Frame HeaderFromBytes(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 '0x"+ rawOpcode.ToString("X") +"' not understood");
-
+ throw new ArgumentException("Opcode '0x" + rawOpcode.ToString("X") + "' not understood");
+
var returnFrame = new Frame {
- IsFinal = (raw[0] & 0x80) != 0,
- Opcode = (kOpcode)rawOpcode,
+ IsFinal = (raw[0] & 0x80) != 0,
+ Opcode = (kOpcode)rawOpcode,
IsMasked = (raw[1] & 0x80) != 0,
Reserved = (byte)((raw[0] & 0x70) >> 4)
};
ulong bodyLength = raw[1] & 0x7Ful;
- int headerOffset =
- bodyLength < 0x7E
- ? 1
+ int headerOffset =
+ bodyLength < 0x7E
+ ? 1
: (bodyLength == 0x7E ? 3 : 9);
-
+
if(raw.Length < headerOffset + 1)
throw new FormatException("Websocket frame is smaller than expected header size");
bodyLength = bodyLength < 0x7E ? 0 : bodyLength;
for(var i = headerOffset - 1; i > 0; --i)
- bodyLength |= (ulong)raw[2 + i] << (8*(headerOffset - 1 - i));
+ bodyLength |= (ulong)raw[2 + i] << (8 * (headerOffset - 1 - i));
- if(returnFrame.IsMasked) {
+ if(bodyLength > Int32.MaxValue)
+ throw new FormatException("Frame is too large to interpret");
+
+ returnFrame._BodyLength = (int)bodyLength;
+
+ if(returnFrame.IsMasked)
Array.Copy(raw, headerOffset + 1, returnFrame.Mask, 0, 4);
- headerOffset += 4;
- }
- ulong expectedFrameLength = bodyLength + (uint)headerOffset + 1;
- if(expectedFrameLength < (ulong)raw.LongLength)
- throw new FormatException("Raw frame length ("+ (ulong)raw.LongLength + ") is less than described size ("+ expectedFrameLength + ")");
-
- Array.Copy(raw, headerOffset + 1, returnFrame.Content, 0L, (long)bodyLength);
+ return returnFrame;
+ }
+
+ public static Frame FromBytes(byte[] raw) {
+ var returnFrame = HeaderFromBytes(raw);
+
+ uint expectedFrameLength = (uint)returnFrame.BodyLength + (uint)returnFrame.HeaderLength;
+ if(expectedFrameLength < (uint)raw.Length)
+ throw new FormatException("Raw frame length ("+ (uint)raw.Length + ") is less than described size ("+ expectedFrameLength + ")");
+
+ returnFrame.Content = new byte[returnFrame.BodyLength];
+ Array.Copy(raw, returnFrame.HeaderLength, returnFrame.Content, 0L, (long)returnFrame.BodyLength);
if(returnFrame.IsMasked)
returnFrame.Content = returnFrame.MaskedContent;
diff --git a/server/Kneesocks/Kneesocks.csproj b/server/Kneesocks/Kneesocks.csproj
index 8d4f9a5..ffa0fec 100644
--- a/server/Kneesocks/Kneesocks.csproj
+++ b/server/Kneesocks/Kneesocks.csproj
@@ -45,6 +45,7 @@
+
diff --git a/server/Kneesocks/ReadBuffer.cs b/server/Kneesocks/ReadBuffer.cs
new file mode 100644
index 0000000..a2d33ee
--- /dev/null
+++ b/server/Kneesocks/ReadBuffer.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Kneesocks {
+ class ReadBuffer {
+ private List Buffer;
+ private int ExpectedLength;
+ private string ExpectedString;
+ private NetworkStream Source;
+ private DateTime StartTime;
+
+ public bool IsReading { get; private set; } = false;
+
+ public ReadBuffer(NetworkStream source) {
+ Source = source;
+ }
+
+ public TimeSpan ElapsedReadTime {
+ get {
+ return DateTime.Now - StartTime;
+ }
+ }
+
+ private byte[] CheckBuffer() {
+ byte[] returnValue = null;
+
+ if(ExpectedString != null) {
+ var location = Encoding.ASCII.GetString(Buffer.ToArray()).IndexOf(ExpectedString);
+ if(location != -1) {
+ var fullJump = location + ExpectedString.Length;
+ returnValue = Buffer.Take(fullJump).ToArray();
+ Buffer = Buffer.Skip(fullJump).ToList();
+ IsReading = false;
+ }
+ } else {
+ if(Buffer.Count >= ExpectedLength) {
+ returnValue = Buffer.Take(ExpectedLength).ToArray();
+ Buffer = Buffer.Skip(ExpectedLength).ToList();
+ IsReading = false;
+ }
+ }
+
+ return returnValue;
+ }
+
+ public byte[] AttemptRead() {
+ if(!IsReading)
+ return null;
+
+ byte[] returnValue;
+ if((returnValue = CheckBuffer()) != null)
+ return returnValue;
+
+ var buffer = new byte[1024];
+ while(Source.DataAvailable) {
+ var readAmount = ExpectedString == null
+ ? Math.Min(1024, ExpectedLength - Buffer.Count)
+ : 1024;
+
+ var bytesRead = Source.Read(buffer, 0, readAmount);
+ if(bytesRead == readAmount)
+ Buffer.AddRange(buffer);
+ else
+ Buffer.AddRange(buffer.Take(readAmount));
+
+ if((returnValue = CheckBuffer()) != null)
+ return returnValue;
+ }
+
+ return null;
+ }
+
+ public byte[] AttemptRead(int length) {
+ if(IsReading)
+ return null;
+
+ IsReading = true;
+ ExpectedLength = length;
+ ExpectedString = null;
+ StartTime = DateTime.Now;
+
+ return AttemptRead();
+ }
+
+ public byte[] AttemptRead(string terminator) {
+ if(IsReading)
+ return null;
+
+ IsReading = true;
+ ExpectedString = terminator;
+ StartTime = DateTime.Now;
+
+ return AttemptRead();
+ }
+ }
+}
diff --git a/server/Kneesocks/Stack.cs b/server/Kneesocks/Stack.cs
index d9d7d65..55fba2b 100644
--- a/server/Kneesocks/Stack.cs
+++ b/server/Kneesocks/Stack.cs
@@ -9,7 +9,6 @@ namespace Kneesocks {
public class Stack where T : Connection {
private Pool PoolRef = null;
private List Clients = new List();
- private Mutex ClientsMutex = new Mutex();
private bool RunWithNoClients = false;
private bool Running = true;
private bool _finished = false;
@@ -26,14 +25,10 @@ namespace Kneesocks {
RunWithNoClients = runWithNoClients;
}
- public bool AddClient(Connection client) {
- if(!ClientsMutex.WaitOne(5000))
- return false;
-
- Clients.Add(client);
-
- ClientsMutex.ReleaseMutex();
- return true;
+ public void AddClient(Connection client) {
+ lock(Clients) {
+ Clients.Add(client);
+ }
}
public int Count {