SOCKS FINALLY DONE

HOLY SHIT
This commit is contained in:
Malloc of Kuzkycyziklistan 2017-05-12 16:05:18 -05:00
parent c8d100da96
commit 132e272580
9 changed files with 194 additions and 59 deletions

View file

@ -18,6 +18,9 @@ namespace CircleScape {
// logic processing loop // logic processing loop
} }
server.Stop();
PoolManager.Dispose();
} }
} }
} }

View file

@ -9,7 +9,9 @@ using System.IO;
namespace Kneesocks { namespace Kneesocks {
public class Connection { public class Connection {
public UInt64? _Id = null; private bool Initialized = false;
private UInt64? _Id = null;
public UInt64 Id { public UInt64 Id {
get { get {
if(_Id == null) if(_Id == null)
@ -22,9 +24,14 @@ namespace Kneesocks {
_Id = value; _Id = value;
} }
} }
internal bool IsIdNull {
get {
return _Id == null;
}
}
private TcpClient Socket; private TcpClient Socket = null;
private NetworkStream Stream; private NetworkStream Stream = null;
ReadBuffer Buffer; ReadBuffer Buffer;
private byte[] FirstTwoBytes = null; private byte[] FirstTwoBytes = null;
@ -32,27 +39,54 @@ namespace Kneesocks {
private byte[] FrameHeader = null; private byte[] FrameHeader = null;
private List<Frame> ReceiveFrameBuffer = new List<Frame>(); private List<Frame> ReceiveFrameBuffer = new List<Frame>();
private List<Frame> SendFrameBuffer = new List<Frame>(); private List<Frame> SendFrameBuffer = new List<Frame>();
private const int MaximumSendFrameSize = 0xFFFFF;
private Random Random = new Random();
protected const int PingInterval = 30;
protected const int TimeoutInterval = 120;
private byte[] PingData = Encoding.ASCII.GetBytes("woomy!");
private DateTime LastPing; private DateTime LastPing;
private bool AwaitingPingResponse = false;
private TimeSpan TimeSinceLastPing {
get {
return DateTime.UtcNow - LastPing;
}
}
internal bool OutsidePool = false;
public bool Disconnected { get; private set; } = false; public bool Disconnected { get; private set; } = false;
public string DisconnectReason { get; private set; } = null; public string DisconnectReason { get; private set; } = null;
public bool Handshaked { get; private set; } = false; public bool Handshaked { get; private set; } = false;
public Handshake ClientHandshake { get; private set; } = null; public Handshake ClientHandshake { get; private set; } = null;
public Connection(TcpClient sock) { public void Initialize(TcpClient sock) {
if(Initialized)
return;
Socket = sock; Socket = sock;
Socket.ReceiveTimeout = 1; Socket.ReceiveTimeout = 1;
Stream = sock.GetStream(); Stream = sock.GetStream();
Buffer = new ReadBuffer(Stream); Buffer = new ReadBuffer(Stream);
Initialized = true;
} }
public Connection(UInt64 id, TcpClient sock) : this(sock) { public void Initialize(UInt64 id, TcpClient sock) {
if(Initialized)
return;
Initialize(sock);
Id = id; Id = id;
Initialized = true;
} }
public Connection(Connection conn, bool preserveId = true) { public void Initialize(Connection conn, bool preserveId = false) {
if(Initialized)
return;
if(preserveId) if(preserveId)
_Id = conn._Id; _Id = conn._Id;
@ -65,22 +99,25 @@ namespace Kneesocks {
FrameHeader = conn.FrameHeader; FrameHeader = conn.FrameHeader;
ReceiveFrameBuffer = conn.ReceiveFrameBuffer; ReceiveFrameBuffer = conn.ReceiveFrameBuffer;
LastPing = conn.LastPing;
Disconnected = conn.Disconnected; Disconnected = conn.Disconnected;
DisconnectReason = conn.DisconnectReason; DisconnectReason = conn.DisconnectReason;
Handshaked = conn.Handshaked; Handshaked = conn.Handshaked;
ClientHandshake = conn.ClientHandshake; ClientHandshake = conn.ClientHandshake;
Initialized = true;
} }
private const int MaximumFrameSize = 0xFFFFF;
private void _Send(byte[] message, bool isFinal = true, bool singleFrame = false, bool first = false) { private void _Send(byte[] message, bool isFinal = true, bool singleFrame = false, bool first = false) {
int frameCount = singleFrame ? 0 : (message.Length / MaximumFrameSize); int frameCount = singleFrame ? 0 : (message.Length / MaximumSendFrameSize);
for(var i = 0; i <= frameCount; ++i) { for(var i = 0; i <= frameCount; ++i) {
SendFrameBuffer.Add(new Frame { SendFrameBuffer.Add(new Frame {
IsFinal = (i == frameCount && isFinal) ? true : false, IsFinal = (i == frameCount && isFinal) ? true : false,
IsMasked = false, IsMasked = false,
Opcode = (i == 0 || (singleFrame && first)) ? Frame.kOpcode.BinaryFrame : Frame.kOpcode.Continuation, Opcode = (i == 0 || (singleFrame && first)) ? Frame.kOpcode.BinaryFrame : Frame.kOpcode.Continuation,
Content = message.Subset(i * (MaximumFrameSize + 1), MaximumFrameSize) Content = message.Subset(i * (MaximumSendFrameSize + 1), MaximumSendFrameSize)
}); });
} }
} }
@ -96,15 +133,15 @@ namespace Kneesocks {
return; return;
bool firstRead = true; bool firstRead = true;
byte[] byteBuffer = new byte[MaximumFrameSize]; byte[] byteBuffer = new byte[MaximumSendFrameSize];
while(true) { while(true) {
var bytesRead = stream.Read(byteBuffer, 0, MaximumFrameSize); var bytesRead = stream.Read(byteBuffer, 0, MaximumSendFrameSize);
if(stream.Position == stream.Length) { if(stream.Position == stream.Length) {
_Send(bytesRead == MaximumFrameSize ? byteBuffer : byteBuffer.Take(bytesRead).ToArray(), true, true, firstRead); _Send(bytesRead == MaximumSendFrameSize ? byteBuffer : byteBuffer.Take(bytesRead).ToArray(), true, true, firstRead);
return; return;
} else { } else {
_Send(bytesRead == MaximumFrameSize ? byteBuffer : byteBuffer.Take(bytesRead).ToArray(), false, true, firstRead); _Send(bytesRead == MaximumSendFrameSize ? byteBuffer : byteBuffer.Take(bytesRead).ToArray(), false, true, firstRead);
} }
firstRead = false; firstRead = false;
@ -133,9 +170,25 @@ namespace Kneesocks {
buffer = buffer == null ? Buffer.AttemptRead(terminator) buffer = buffer == null ? Buffer.AttemptRead(terminator)
: buffer; : buffer;
} }
public void Parse() { internal void Parse() {
if(Handshaked) { if(Handshaked) {
if(!Buffer.IsReading) {
if(TimeSinceLastPing.Seconds > TimeoutInterval) {
Disconnect(Frame.kClosingReason.Normal, "Ping response timed out.");
} else if(TimeSinceLastPing.Seconds > PingInterval && !AwaitingPingResponse) {
var frameBytes = new Frame {
IsFinal = true,
IsMasked = false,
Opcode = Frame.kOpcode.Ping,
Content = PingData
}.GetBytes();
Stream.Write(frameBytes, 0, frameBytes.Length);
AwaitingPingResponse = true;
}
}
lock(SendFrameBuffer) { lock(SendFrameBuffer) {
if(SendFrameBuffer.Count > 0) { if(SendFrameBuffer.Count > 0) {
foreach(var frame in SendFrameBuffer) { foreach(var frame in SendFrameBuffer) {
@ -172,6 +225,7 @@ namespace Kneesocks {
Stream.Write(response, 0, response.Length); Stream.Write(response, 0, response.Length);
ClientHandshake = request; ClientHandshake = request;
Handshaked = true; Handshaked = true;
LastPing = DateTime.UtcNow; LastPing = DateTime.UtcNow;
} catch(Exception e) { } catch(Exception e) {
Disconnect(Frame.kClosingReason.ProtocolError, e.Message); Disconnect(Frame.kClosingReason.ProtocolError, e.Message);
@ -238,7 +292,8 @@ namespace Kneesocks {
if(tempFrame.IsFinal) { if(tempFrame.IsFinal) {
switch(tempFrame.Opcode) { switch(tempFrame.Opcode) {
case Frame.kOpcode.Ping: case Frame.kOpcode.Ping:
LastPing = DateTime.Now; LastPing = DateTime.UtcNow;
AwaitingPingResponse = false;
tempFrame.Opcode = Frame.kOpcode.Pong; tempFrame.Opcode = Frame.kOpcode.Pong;
var pingBuffer = tempFrame.GetBytes(); var pingBuffer = tempFrame.GetBytes();
@ -246,24 +301,33 @@ namespace Kneesocks {
break; break;
case Frame.kOpcode.Pong: case Frame.kOpcode.Pong:
LastPing = DateTime.Now; LastPing = DateTime.UtcNow;
AwaitingPingResponse = false;
break; break;
case Frame.kOpcode.Close: case Frame.kOpcode.Close:
Disconnect(Frame.kClosingReason.Normal, "Connection closed."); Disconnect(Frame.kClosingReason.Normal, "Connection closed.");
break; break;
case Frame.kOpcode.BinaryFrame:
case Frame.kOpcode.TextFrame:
case Frame.kOpcode.Continuation:
byte[] byteBuffer = new byte[0];
foreach(var frame in ReceiveFrameBuffer)
byteBuffer = byteBuffer.Concat(frame.Content).ToArray();
ReceiveFrameBuffer = new List<Frame>();
OnReceive(byteBuffer);
break;
} }
byte[] byteBuffer = new byte[0];
foreach(var frame in ReceiveFrameBuffer)
byteBuffer = byteBuffer.Concat(frame.Content).ToArray();
ReceiveFrameBuffer = new List<Frame>();
OnReceive(byteBuffer);
} }
} }
} }
public void RemoveFromPool() {
OutsidePool = true;
}
public void Disconnect(string reason = null) { public void Disconnect(string reason = null) {
Disconnect(Frame.kClosingReason.Normal, reason); Disconnect(Frame.kClosingReason.Normal, reason);
} }

View file

@ -26,6 +26,7 @@ namespace Kneesocks {
// 0 means never redistribute // 0 means never redistribute
public int Tolerance { get; set; } = 0; public int Tolerance { get; set; } = 0;
private bool Disposed = false;
private int _fullThreadCount; private int _fullThreadCount;
private volatile bool updateFullThreadCount = true; private volatile bool updateFullThreadCount = true;
@ -41,42 +42,59 @@ namespace Kneesocks {
CreateThread(runWithNoClients: true); CreateThread(runWithNoClients: true);
} }
private void IndexConnection(UInt64 id, T connection) { public T this[UInt64 id] {
lock(Connections) { get {
if(id == 0) if(HasConnection(id))
id = (ulong)Interlocked.Increment(ref InternalCounter); return Connections[id];
else return null;
connection.Id = id;
Connections.Add(id, connection);
} }
} }
public void InvalidateConnection(UInt64 id) { public bool HasConnection(UInt64 id) {
return Connections.ContainsKey(id);
}
private void IndexConnection(T connection) {
lock(Connections) {
if(connection.IsIdNull)
connection.Id = (ulong)Interlocked.Increment(ref InternalCounter);
Connections.Add(connection.Id, connection);
}
}
internal void InvalidateConnection(UInt64 id) {
lock(Connections) { lock(Connections) {
Connections.Remove(id); Connections.Remove(id);
} }
} }
public void Broadcast(byte[] message) { public void Broadcast(byte[] message) {
if(Disposed)
return;
lock(Connections) { lock(Connections) {
foreach(var conn in Connections) foreach(var conn in Connections)
conn.Value.Send(message); conn.Value.Send(message);
} }
} }
public bool AddConnection(T connection, UInt64 id = 0) { public bool AddConnection(T connection) {
if(Disposed)
return false;
lock(Threads) { lock(Threads) {
foreach(var thread in Threads) { foreach(var thread in Threads) {
if(thread.Stack.Count < FullThreadSize) { if(thread.Stack.Count < FullThreadSize) {
thread.Stack.AddClient(connection); thread.Stack.AddClient(connection);
IndexConnection(id, connection); IndexConnection(connection);
return true; return true;
} }
} }
if(MaxCount == 0 || Threads.Count < MaxCount) { if(MaxCount == 0 || Threads.Count < MaxCount) {
CreateThread(connection); CreateThread(connection);
IndexConnection(id, connection); IndexConnection(connection);
return true; return true;
} }
} }
@ -84,7 +102,7 @@ namespace Kneesocks {
return false; return false;
} }
public void InvalidateThread(Stack<T> stackRef) { internal void InvalidateThread(Stack<T> stackRef) {
lock(Threads) { lock(Threads) {
var ctx = Threads.FirstOrDefault(x => Object.ReferenceEquals(x.Stack, stackRef)); var ctx = Threads.FirstOrDefault(x => Object.ReferenceEquals(x.Stack, stackRef));
if(ctx != null) { if(ctx != null) {
@ -120,6 +138,37 @@ namespace Kneesocks {
} }
} }
public void Dispose() {
if(Disposed)
return;
Disposed = true;
lock(Threads) {
foreach(var thread in Threads)
thread.Stack.StopThread();
}
while(true) {
Thread.Sleep(100);
lock(Threads) {
if(Threads.Count == 0)
break;
}
}
lock(Connections) {
foreach(var conn in Connections) {
conn.Value.Disconnect(Frame.kClosingReason.Normal, "Server shutting down.");
}
}
}
~Pool() {
Dispose();
}
class ThreadContext { class ThreadContext {
public Thread Thread { get; set; } public Thread Thread { get; set; }
public Stack<T> Stack { get; set; } public Stack<T> Stack { get; set; }

View file

@ -7,11 +7,11 @@ using System.Net.Sockets;
using System.Net; using System.Net;
namespace Kneesocks { namespace Kneesocks {
public class Server<T> where T : Connection { public class Server<T> where T : Connection, new() {
private TcpListener Socket; private TcpListener Socket;
private Thread Listener = null; private Thread Listener = null;
private Pool<T> ConnectionPool = null; private Pool<T> ConnectionPool = null;
private bool Started = false; public bool Started { get; private set; } = false;
public UInt16 Port { get; private set; } public UInt16 Port { get; private set; }
public Server(UInt16 port, Pool<T> pool) { public Server(UInt16 port, Pool<T> pool) {
@ -39,8 +39,11 @@ namespace Kneesocks {
Socket.Start(); Socket.Start();
while(Started) { while(Started) {
if(Socket.Pending()) if(Socket.Pending()) {
ConnectionPool.AddConnection((T)new Connection(Socket.AcceptTcpClient())); var templatedConnection = new T();
templatedConnection.Initialize(Socket.AcceptTcpClient());
ConnectionPool.AddConnection(templatedConnection);
}
Thread.Sleep(100); Thread.Sleep(100);
} }

View file

@ -6,12 +6,14 @@ using System.Text;
using System.Threading; using System.Threading;
namespace Kneesocks { namespace Kneesocks {
public class Stack<T> where T : Connection { internal class Stack<T> where T : Connection {
private Pool<T> PoolRef = null; private Pool<T> PoolRef = null;
private List<T> Clients = new List<T>(); private List<T> Clients = new List<T>();
private bool RunWithNoClients = false; private bool RunWithNoClients = false;
private bool Running = true; private bool Running = true;
public bool Finished { get; private set; } = false;
public Stack(Pool<T> poolRef, T initialConnection = null) { public Stack(Pool<T> poolRef, T initialConnection = null) {
PoolRef = poolRef; PoolRef = poolRef;
if(initialConnection != null) if(initialConnection != null)
@ -36,31 +38,26 @@ namespace Kneesocks {
} }
} }
public void StopThread() { internal void StopThread() {
Running = false; Running = false;
} }
public bool Finished { get; private set; } private bool CheckIfConnected(T client) {
return !client.Disconnected && !client.OutsidePool;
public bool UnlistConnection(Connection connection) {
lock(Clients) {
foreach(var conn in Clients) {
}
}
} }
// USED FOR THREADING -- DO NOT CALL
public void ManageStack() { public void ManageStack() {
while(Running && (Count > 0 || RunWithNoClients)) { while(Running && (Count > 0 || RunWithNoClients)) {
lock(Clients) { lock(Clients) {
for(var i = Count - 1; i >= 0 && Running; --i) { for(var i = Count - 1; i >= 0 && Running; --i) {
var client = Clients[i]; var client = Clients[i];
var connected = !client.Disconnected; var connected = CheckIfConnected(client);
if(connected) { if(connected) {
try { try {
client.Parse(); client.Parse();
if(CheckIfConnected(client))
connected = false;
} catch { } catch {
connected = false; connected = false;
} }

View file

@ -31,7 +31,7 @@ namespace Square {
return BitConverter.ToString(bytes).Replace("-", ""); return BitConverter.ToString(bytes).Replace("-", "");
} }
public static string ToString(this byte[] bytes, bool isUtf8 = true) { public static string GetString(this byte[] bytes, bool isUtf8 = true) {
return isUtf8 ? Encoding.UTF8.GetString(bytes) return isUtf8 ? Encoding.UTF8.GetString(bytes)
: Encoding.ASCII.GetString(bytes); : Encoding.ASCII.GetString(bytes);
} }

View file

@ -7,7 +7,6 @@ using System.Threading.Tasks;
namespace CircleScape { namespace CircleScape {
class ActiveConnection : Kneesocks.Connection { class ActiveConnection : Kneesocks.Connection {
public ActiveConnection(UInt32 id, TcpClient sock) : base(id, sock) { }
public ActiveConnection(PendingConnection conn) : base(conn) { }
} }
} }

View file

@ -4,10 +4,25 @@ using System.Linq;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Square;
using Kneesocks;
namespace CircleScape { namespace CircleScape {
class PendingConnection : Kneesocks.Connection { class PendingConnection : Connection {
public PendingConnection(UInt32 id, TcpClient sock) : base(id, sock) { } private DateTime ConnectionOpened;
protected override void OnOpen() {
ConnectionOpened = DateTime.UtcNow;
}
protected override void OnParse() {
if((DateTime.UtcNow - ConnectionOpened).Seconds > 60) {
Disconnect(Frame.kClosingReason.ProtocolError, "Logon request timed out.");
}
}
protected override void OnReceive(byte[] data) {
Console.WriteLine(Id + " says " + data.GetString());
}
} }
} }

View file

@ -32,5 +32,10 @@ namespace CircleScape {
ActiveConnectionsPool = new Pool<ActiveConnection>(); ActiveConnectionsPool = new Pool<ActiveConnection>();
} }
public static void Dispose() {
PendingConnectionsPool.Dispose();
ActiveConnectionsPool.Dispose();
}
} }
} }