diff --git a/server/CircleScape.csproj b/server/CircleScape.csproj index 8ce6bfa..d5d8c08 100644 --- a/server/CircleScape.csproj +++ b/server/CircleScape.csproj @@ -101,7 +101,6 @@ - @@ -125,12 +124,6 @@ - - - {347353f6-cecd-4895-8717-2b5394ac71cc} - Kneesocks - - @@ -150,6 +143,16 @@ App.config + + + {347353f6-cecd-4895-8717-2b5394ac71cc} + Kneesocks + + + {054f172e-9683-40bc-8bdd-7671340ec193} + Square + + diff --git a/server/CircleScape.sln b/server/CircleScape.sln index c633273..3efd8d2 100644 --- a/server/CircleScape.sln +++ b/server/CircleScape.sln @@ -5,7 +5,9 @@ VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CircleScape", "CircleScape.csproj", "{438DBAC1-BA37-40BB-9CCE-0FE1F23C6DC5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kneesocks", "Kneesocks\Kneesocks.csproj", "{347353F6-CECD-4895-8717-2B5394AC71CC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kneesocks", "Libraries\Kneesocks\Kneesocks.csproj", "{347353F6-CECD-4895-8717-2B5394AC71CC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Square", "Libraries\Square\Square.csproj", "{054F172E-9683-40BC-8BDD-7671340EC193}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +23,10 @@ Global {347353F6-CECD-4895-8717-2B5394AC71CC}.Debug|Any CPU.Build.0 = Debug|Any CPU {347353F6-CECD-4895-8717-2B5394AC71CC}.Release|Any CPU.ActiveCfg = Release|Any CPU {347353F6-CECD-4895-8717-2B5394AC71CC}.Release|Any CPU.Build.0 = Release|Any CPU + {054F172E-9683-40BC-8BDD-7671340EC193}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {054F172E-9683-40BC-8BDD-7671340EC193}.Debug|Any CPU.Build.0 = Debug|Any CPU + {054F172E-9683-40BC-8BDD-7671340EC193}.Release|Any CPU.ActiveCfg = Release|Any CPU + {054F172E-9683-40BC-8BDD-7671340EC193}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/server/Kneesocks/Utilities.cs b/server/Kneesocks/Utilities.cs deleted file mode 100644 index 9958031..0000000 --- a/server/Kneesocks/Utilities.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Kneesocks { - public static class StringExtensions { - public enum kHashReturnType { - RAW, HEX, BASE64 - } - - public static byte[] GetBytes(this string str, bool isUtf8 = true) { - return isUtf8 ? Encoding.UTF8.GetBytes(str) - : Encoding.ASCII.GetBytes(str); - } - - public static int ByteLength(this string str, bool isUtf8 = true) { - return isUtf8 ? Encoding.UTF8.GetByteCount(str) - : Encoding.ASCII.GetByteCount(str); - } - - public static string Base64Encode(this string str, bool isUtf8 = true) { - var raw = - isUtf8 ? Encoding.UTF8.GetBytes(str) - : Encoding.ASCII.GetBytes(str); - return Convert.ToBase64String(raw); - } - - public static string Base64Decode(this string str, bool isUtf8 = true) { - var raw = Convert.FromBase64String(str); - return isUtf8 ? Encoding.UTF8.GetString(raw) - : Encoding.ASCII.GetString(raw); - } - - public static string SHA1(this string str, kHashReturnType type = kHashReturnType.RAW) { - using(var hasher = System.Security.Cryptography.SHA1.Create()) { - return ParseRawHash( - hasher.ComputeHash(str.GetBytes(false)), - type - ); - } - } - - public static string MD5(this string str, kHashReturnType type = kHashReturnType.RAW) { - using(var hasher = System.Security.Cryptography.MD5.Create()) { - return ParseRawHash( - hasher.ComputeHash(str.GetBytes(false)), - type - ); - } - } - - private static string ParseRawHash(byte[] hash, kHashReturnType type) { - var raw = Encoding.ASCII.GetString(hash); - - switch(type) { - case kHashReturnType.BASE64: - return Base64Encode(raw, false); - case kHashReturnType.HEX: - return BitConverter.ToString(hash).Replace("-", ""); - case kHashReturnType.RAW: - default: - return raw; - } - } - } - - public static class NumericExtensions { - /*public static T Unpack(this byte[] bytes, int offset = 0) { - return 0; - }*/ - } -} diff --git a/server/Kneesocks/Connection.cs b/server/Libraries/Kneesocks/Connection.cs similarity index 71% rename from server/Kneesocks/Connection.cs rename to server/Libraries/Kneesocks/Connection.cs index 81a56e0..afd1aa6 100644 --- a/server/Kneesocks/Connection.cs +++ b/server/Libraries/Kneesocks/Connection.cs @@ -32,9 +32,7 @@ namespace Kneesocks { public string DisconnectReason { get; private set; } = null; public bool Handshaked { get; private set; } = false; - private string RawClientHandshake = ""; - private Dictionary Headers = - new Dictionary(StringComparer.OrdinalIgnoreCase); + public Handshake ClientHandshake { get; private set; } = null; public Connection(TcpClient sock) { Socket = sock; @@ -62,30 +60,51 @@ namespace Kneesocks { DisconnectReason = conn.DisconnectReason; Handshaked = conn.Handshaked; - RawClientHandshake = conn.RawClientHandshake; - Headers = conn.Headers; - } - - private void StartRead(ulong length) { - + ClientHandshake = conn.ClientHandshake; } public byte[] Parse() { byte[] readBuffer = null; if(Buffer.IsReading) { readBuffer = Buffer.AttemptRead(); - if(readBuffer == null) + if(readBuffer == null) { + if(Buffer.ElapsedReadTime.Seconds > 30) + Disconnect(Frame.kClosingReason.ProtocolError, "Timed out waiting for a full response"); + return null; + } } if(!Handshaked) { - if(Stream.) - - return null; - } else { + if(!Buffer.IsReading) { + readBuffer = Buffer.AttemptRead("\r\n\r\n"); + if(readBuffer == null) + return null; + } - OnParse(); + try { + Handshake request = new Handshake(Encoding.ASCII.GetString(readBuffer)); + var response = Handshake.AcceptRequest(request).ToBytes(); + Stream.Write(response, 0, response.Length); + ClientHandshake = request; + Handshaked = true; + } catch(Exception e) { + Disconnect(Frame.kClosingReason.ProtocolError, e.Message); + return null; + } + + OnOpen(); + return null; } + + /*if(!Buffer.IsReading) { + readBuffer = Buffer.AttemptRead("\r\n\r\n"); + if(readBuffer == null) + return null; + }*/ + + OnParse(); + return null; } public void Disconnect(string reason = null) { diff --git a/server/Kneesocks/Frame.cs b/server/Libraries/Kneesocks/Frame.cs similarity index 100% rename from server/Kneesocks/Frame.cs rename to server/Libraries/Kneesocks/Frame.cs diff --git a/server/Kneesocks/Handshake.cs b/server/Libraries/Kneesocks/Handshake.cs similarity index 90% rename from server/Kneesocks/Handshake.cs rename to server/Libraries/Kneesocks/Handshake.cs index e057427..b6cb636 100644 --- a/server/Kneesocks/Handshake.cs +++ b/server/Libraries/Kneesocks/Handshake.cs @@ -27,7 +27,7 @@ namespace Kneesocks { Service_Unavailable = 503, Gateway_Timeout = 504 } - + public kStatusCode StatusCode { get; private set; } = kStatusCode.Switching_Protocols; protected string StatusCodeText { get { @@ -44,25 +44,23 @@ namespace Kneesocks { throw new FormatException("Header delimeter not found in raw data"); var header = rawData.Substring(0, headerLength); - if(!header.StartsWith("HTTP/")) + if(!header.StartsWith("GET ") || !header.Contains("HTTP/")) throw new FormatException("Protocol defined in status line not understood"); var lines = header.Split('\n'); foreach(var line in lines) { string[] parts; - if(line.StartsWith("HTTP/")) { + if(line.StartsWith("GET ")) { parts = line.Trim().Split(' '); - if(parts.Length < 2) + if(parts.Length < 3) throw new FormatException("Status line in header malformed"); - int code; + /*int code; if(!int.TryParse(parts[1], out code)) - throw new FormatException("Status code sent is not a number"); + throw new FormatException("Status code sent is not a number");*/ - if(!Enum.IsDefined(typeof(kStatusCode), code)) - throw new NotSupportedException("Status code not supported"); - - StatusCode = (kStatusCode)code; + /*if(!Enum.IsDefined(typeof(kStatusCode), code)) + throw new NotSupportedException("Status code not supported");*/ } else { parts = line.Trim().Split(new char[] {':'}, 2); if(parts.Length == 2) @@ -88,6 +86,8 @@ namespace Kneesocks { var key = request.GetHeader("Sec-WebSocket-Key"); var connectionHash = (key + nonce).SHA1().Base64Encode(false); + var test = ("dGhlIHNhbXBsZSBub25jZQ==" + nonce).SHA1().Base64Encode(false); + var shake = new Handshake(kStatusCode.Switching_Protocols); shake.SetHeader("Upgrade", "websocket") .SetHeader("Connection", "Upgrade") @@ -99,6 +99,10 @@ namespace Kneesocks { return new Handshake(statusCode, message); } + public byte[] ToBytes() { + return Encoding.ASCII.GetBytes(ToString()); + } + public override string ToString() { if(Content != null) { SetHeader("Content-Length", Content.ByteLength().ToString()); diff --git a/server/Kneesocks/Kneesocks.csproj b/server/Libraries/Kneesocks/Kneesocks.csproj similarity index 92% rename from server/Kneesocks/Kneesocks.csproj rename to server/Libraries/Kneesocks/Kneesocks.csproj index ffa0fec..d37edf7 100644 --- a/server/Kneesocks/Kneesocks.csproj +++ b/server/Libraries/Kneesocks/Kneesocks.csproj @@ -50,6 +50,12 @@ + + + {054f172e-9683-40bc-8bdd-7671340ec193} + Square + + + \ No newline at end of file diff --git a/server/Libraries/Square/StringExtensions.cs b/server/Libraries/Square/StringExtensions.cs new file mode 100644 index 0000000..7609144 --- /dev/null +++ b/server/Libraries/Square/StringExtensions.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Square { + public static class StringExtensions { + public static byte[] GetBytes(this string str, bool isUtf8 = true) { + return isUtf8 ? Encoding.UTF8.GetBytes(str) + : Encoding.ASCII.GetBytes(str); + } + + public static int ByteLength(this string str, bool isUtf8 = true) { + return isUtf8 ? Encoding.UTF8.GetByteCount(str) + : Encoding.ASCII.GetByteCount(str); + } + + public static string Base64Encode(this string str, bool isUtf8 = true) { + var raw = + isUtf8 ? Encoding.UTF8.GetBytes(str) + : Encoding.ASCII.GetBytes(str); + return Convert.ToBase64String(raw); + } + + public static string Base64Decode(this string str, bool isUtf8 = true) { + var raw = Convert.FromBase64String(str); + return isUtf8 ? Encoding.UTF8.GetString(raw) + : Encoding.ASCII.GetString(raw); + } + + public static byte[] Base64DecodeRaw(this string str) { + return Convert.FromBase64String(str); + } + } +} diff --git a/server/Utilities.cs b/server/Utilities.cs deleted file mode 100644 index 1d6e2cf..0000000 --- a/server/Utilities.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; - -namespace CircleScape { - public static class StringExtensions { - public enum kHashReturnType { - RAW, HEX, BASE64 - } - - public static string Base64Encode(this string str, bool isUtf8 = true) { - var raw = - isUtf8 ? Encoding.UTF8.GetBytes(str) - : Encoding.ASCII.GetBytes(str); - return Convert.ToBase64String(raw); - } - - public static string Base64Decode(this string str, bool isUtf8 = true) { - var raw = Convert.FromBase64String(str); - return isUtf8 ? Encoding.UTF8.GetString(raw) - : Encoding.ASCII.GetString(raw); - } - - public static string SHA1(this string str, kHashReturnType type = kHashReturnType.RAW) { - using(var hasher = System.Security.Cryptography.SHA1.Create()) { - return ParseRawHash( - hasher.ComputeHash(Encoding.ASCII.GetBytes(str)), - type - ); - } - } - - public static string MD5(this string str, kHashReturnType type = kHashReturnType.RAW) { - using(var hasher = System.Security.Cryptography.MD5.Create()) { - return ParseRawHash( - hasher.ComputeHash(Encoding.ASCII.GetBytes(str)), - type - ); - } - } - - private static string ParseRawHash(byte[] hash, kHashReturnType type) { - var raw = Encoding.ASCII.GetString(hash); - - switch(type) { - case kHashReturnType.BASE64: - return Base64Encode(raw, false); - case kHashReturnType.HEX: - return BitConverter.ToString(hash).Replace("-", ""); - case kHashReturnType.RAW: - default: - return raw; - } - } - } - - public static class NumericExtensions { - - } -}