diff --git a/protocol.md b/protocol.md index 5892067..847d676 100644 --- a/protocol.md +++ b/protocol.md @@ -271,16 +271,31 @@ Communication between the master server and clients will be done over a WebSocke # Region Type + if 1 Succeeded Boolean + - + 2 Message String + R1 + + + 2 + Session Id + Packed Unsigned Long + ¬R1 + + + 3 + Secret + Bytes (16) + ¬R1 @@ -430,8 +445,12 @@ Communication between the master server and clients will be done over a WebSocke ID 4: Server List Request
[Encrypted] Requester
- Bodyless Packet + + + Bodyless Packet + + diff --git a/server/Encryption/BlockCipher.cs b/server/Encryption/BlockCipher.cs index 1efe02a..b989919 100644 --- a/server/Encryption/BlockCipher.cs +++ b/server/Encryption/BlockCipher.cs @@ -51,7 +51,7 @@ namespace SockScape.Encryption { try { var ms = new MemoryStream(data); var cs = new CryptoStream(ms, - new TripleDESCryptoServiceProvider().CreateEncryptor(Key, IV), + new TripleDESCryptoServiceProvider().CreateDecryptor(Key, IV), CryptoStreamMode.Read); byte[] ret = new byte[data.Length]; diff --git a/server/Entrypoint.cs b/server/Entrypoint.cs index 3ac68a4..d022542 100644 --- a/server/Entrypoint.cs +++ b/server/Entrypoint.cs @@ -48,8 +48,10 @@ namespace SockScape { serverHandle.Start(); } - //var server = new Server(6770, PoolManager.Pending); - //server.Start(); + if(Configuration.General["Run Master"]) + MasterIntraServer.Initialize(); + + MasterIntraClient.Initialize(); /*while(true) { var send = Console.ReadLine(); @@ -60,7 +62,9 @@ namespace SockScape { Console.ReadLine(); - //server.Stop(); + MasterIntraClient.Close(); + if(Configuration.General["Run Master"]) + MasterIntraServer.Close(); } } } diff --git a/server/Libraries/Glove/INI/Value.cs b/server/Libraries/Glove/INI/Value.cs index 5aaf319..bd98fec 100644 --- a/server/Libraries/Glove/INI/Value.cs +++ b/server/Libraries/Glove/INI/Value.cs @@ -21,6 +21,9 @@ namespace Glove.INI { public double Dbl => this; + public bool Bool + => this; + public static implicit operator string(Value value) => value.Raw; diff --git a/server/MasterServerList.cs b/server/MasterServerList.cs index cd3980e..c1cc0d1 100644 --- a/server/MasterServerList.cs +++ b/server/MasterServerList.cs @@ -2,29 +2,39 @@ using System.Collections.Generic; using System.Linq; using System.Net; +using System.Runtime.Remoting; using System.Text; using System.Threading.Tasks; using Glove; namespace SockScape { static class MasterServerList { - public static Dictionary Servers { get; } + private static Dictionary _Servers = new Dictionary(); + public static Dictionary Servers { + get { + lock(_Servers) { + return _Servers.ToDictionary(x => x.Key, + x => x.Value); + } + } + } + public static void Write(Server server) { - lock(Servers) { - if(HasId(server.Id) && !Servers[server.Id].Address.Equals(server.Address)) + lock(_Servers) { + if(HasId(server.Id) && !_Servers[server.Id].Address.Equals(server.Address)) Console.WriteLine($"{DateTime.Now.ToShortTimeString()} - Server {server.Id} has changed IP addresses."); - Servers[server.Id] = server; + _Servers[server.Id] = server; } } public static Packet ReportPacket { get { - lock(Servers) { - var packet = new Packet(kInterMasterId.ServerListing, ((ushort)Servers.Count).Pack()); - foreach(var server in Servers) + lock(_Servers) { + var packet = new Packet(kInterMasterId.ServerListing, ((ushort)_Servers.Count).Pack()); + foreach(var server in _Servers) // TODO change this to support IPv6 packet.AddRegions(server.Key.Pack(), server.Value.UserCount.Pack(), server.Value.Address.MapToIPv4().ToString(), server.Value.Port.Pack()); @@ -34,11 +44,24 @@ namespace SockScape { } } - public static bool HasId(UInt16 id) - => Servers.ContainsKey(id); + public static void RemoveServersByOwners(IEnumerable owners) { + lock(_Servers) { + _Servers = _Servers.Where(x => !owners.Contains(x.Value.Owner)) + .ToDictionary(x => x.Key, x => x.Value); + } + } - public static void Clear() - => Servers.Clear(); + public static bool HasId(UInt16 id) { + lock(_Servers) { + return _Servers.ContainsKey(id); + } + } + + public static void Clear() { + lock(_Servers) { + _Servers.Clear(); + } + } } class Server { @@ -46,5 +69,6 @@ namespace SockScape { public ushort UserCount { get; set; } public IPAddress Address { get; set; } public ushort Port { get; set; } + public MasterIntraServer.Client Owner { get; set; } } } diff --git a/server/SockScape.csproj b/server/SockScape.csproj index a6bbe1a..fdaf8bd 100644 --- a/server/SockScape.csproj +++ b/server/SockScape.csproj @@ -51,7 +51,7 @@ 4 - OnBuildSuccess + Always diff --git a/server/Socks/MasterConnection.cs b/server/Socks/MasterConnection.cs index 3453e80..91afbc0 100644 --- a/server/Socks/MasterConnection.cs +++ b/server/Socks/MasterConnection.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Text; using System.Threading.Tasks; using Kneesocks; using Glove; +using SockScape.DAL; using SockScape.Encryption; namespace SockScape { @@ -18,7 +20,7 @@ namespace SockScape { } protected override void OnParse() { - + } protected override void OnReceive(byte[] data) { @@ -31,6 +33,11 @@ namespace SockScape { return; } + if(packet.Id != (int)kInterMasterId.KeyExchange && Encryptor == null) { + Disconnect(Frame.kClosingReason.ProtocolError, "You must exchange keys before performing any other operations."); + return; + } + switch((kInterMasterId)packet.Id) { case kInterMasterId.KeyExchange: Key.ParseResponsePacket(packet); @@ -42,10 +49,17 @@ namespace SockScape { Encryptor = new StreamCipher(Key.PrivateKey); break; case kInterMasterId.LoginAttempt: + if(packet.RegionCount != 2) + break; + using(var db = new ScapeDb()) { + if(db.Users.) + } break; case kInterMasterId.RegistrationAttempt: - + using(var db = new ScapeDb()) { + + } break; default: Disconnect(Frame.kClosingReason.ProtocolError, "Packet ID could not be understood at this time."); diff --git a/server/Socks/MasterIntraClient.cs b/server/Socks/MasterIntraClient.cs index 1e97c62..ca1a0c1 100644 --- a/server/Socks/MasterIntraClient.cs +++ b/server/Socks/MasterIntraClient.cs @@ -12,6 +12,7 @@ using SockScape.Encryption; namespace SockScape { static class MasterIntraClient { private static Key Key; + private static BlockCipher Encryptor; private static UdpClient Sock; private static Thread ListeningThread; @@ -32,6 +33,12 @@ namespace SockScape { ushort port = (ushort)Configuration.General["Master Port"]; Sock = new UdpClient(Configuration.General["Master Addr"], port); + // TODO figure out what this has to do with ICMP (in server too) + uint IOC_IN = 0x80000000, + IOC_VENDOR = 0x18000000, + SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; + Sock.Client.IOControl((int)SIO_UDP_CONNRESET, new byte[] {0}, null); + Key = new Key(); Encryptor = null; @@ -42,19 +49,22 @@ namespace SockScape { public static void Listener() { while(IsOpen) { - var endPoint = new IPEndPoint(0, 0); + var endPoint = new IPEndPoint(IPAddress.Any, 0); while(Sock.Available > 0) { var data = Sock.Receive(ref endPoint); LastMessageIn = DateTime.Now; + bool readRaw = Encryptor == null + || data.Take(Packet.MagicNumber.Length).SequenceEqual(Packet.MagicNumber); + Packet packet = - Encryptor == null ? Packet.FromBytes(data) - : Packet.FromBytes(Encryptor.Parse(data)); + readRaw ? Packet.FromBytes(data) + : Packet.FromBytes(Encryptor.Decrypt(data)); switch((kIntraMasterId)packet.Id) { case kIntraMasterId.KeyExchange: var responsePacket = Key.ParseRequestPacket(packet); - Encryptor = new StreamCipher(Key.PrivateKey); + Encryptor = new BlockCipher(Key.PrivateKey); if(responsePacket != null) Send(responsePacket); else @@ -62,16 +72,14 @@ namespace SockScape { break; case kIntraMasterId.PositiveAck: - Console.WriteLine($"Packet type {packet[0]} accepted by master"); + Console.WriteLine($"Packet type {packet[0].Raw[0]} accepted by master"); break; case kIntraMasterId.NegativeAck: - Console.WriteLine($"Packet type {packet[0]} declined by master for reason {packet[1]}"); + Console.WriteLine($"Packet type {packet[0].Raw[0]} declined by master for reason {packet[1].Str}"); break; case kIntraMasterId.EncryptionError: - NextSendId = NextRecvId = 0; - Buffer.Clear(); Key = new Key(); Encryptor = null; LastMessageIn = new DateTime(0); @@ -79,12 +87,12 @@ namespace SockScape { } } - if(LastMessageIn.Ticks != 0) { + if(Encryptor != null) { if(DeltaLastOut.TotalSeconds > 2) - Send(Encryptor.Parse(ServerContext.StatusUpdatePacket.GetBytes())); + SendEncrypted(ServerContext.StatusUpdatePacket); } else if(DeltaLastOut.TotalSeconds > 10) - Send(new Packet(kIntraSlaveId.InitiationAttempt, Configuration.General["Master Secret"])); + Send(new Packet(kIntraSlaveId.InitiationAttempt, Configuration.General["Master Secret"].Str)); Thread.Sleep(1); } @@ -94,11 +102,13 @@ namespace SockScape { Send(packet.GetBytes()); } + public static void SendEncrypted(Packet packet) { + Send(Encryptor.Encrypt(packet.GetBytes())); + } + public static void Send(byte[] bytes) { Sock.Send(bytes, bytes.Length); LastMessageOut = DateTime.Now; - Buffer.Add(NextSendId, bytes); - ++NextSendId; } public static void Close() { diff --git a/server/Socks/MasterIntraServer.cs b/server/Socks/MasterIntraServer.cs index ee780f1..0856164 100644 --- a/server/Socks/MasterIntraServer.cs +++ b/server/Socks/MasterIntraServer.cs @@ -12,7 +12,7 @@ using SockScape.DAL; using SockScape.Encryption; namespace SockScape { - static class MasterUdpServer { + static class MasterIntraServer { private static Dictionary Prospects; private static Dictionary Clients; @@ -30,7 +30,13 @@ namespace SockScape { ushort port = (ushort)Configuration.General["Master Port"]; Sock = new UdpClient(new IPEndPoint(IPAddress.Any, port)); - + + // TODO figure out what this has to do with ICMP (in client too) + /*uint IOC_IN = 0x80000000, + IOC_VENDOR = 0x18000000, + SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; + Sock.Client.IOControl((int)SIO_UDP_CONNRESET, new byte[] {0}, null);*/ + IsOpen = true; ListeningThread = new Thread(Listener); ListeningThread.Start(); @@ -38,7 +44,7 @@ namespace SockScape { public static void Listener() { while(IsOpen) { - IPEndPoint endPoint = new IPEndPoint(0, 0); + IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0); while(Sock.Available > 0) { var data = Sock.Receive(ref endPoint); var client = endPoint.ToString(); @@ -48,7 +54,7 @@ namespace SockScape { Packet packet = encryptor == null ? Packet.FromBytes(data) - : Packet.FromBytes(encryptor.Parse(data)); + : Packet.FromBytes(encryptor.Decrypt(data)); if(packet == null) { if(encryptor != null) @@ -56,7 +62,11 @@ namespace SockScape { continue; } - Clients[client].LastReceive = DateTime.Now; + if(IsProspectConnected(client) && encryptor == null) + Prospects[client].LastReceive = DateTime.Now; + else if(IsClientConnected(client) && encryptor != null) + Clients[client].LastReceive = DateTime.Now; + switch((kIntraSlaveId)packet.Id) { case kIntraSlaveId.InitiationAttempt: if(packet.RegionCount != 1 || IsProspectConnected(client)) @@ -81,7 +91,7 @@ namespace SockScape { var privateKey = Prospects[client].Key.ParseResponsePacket(packet); if(privateKey != -1) { Prospects[client].LastReceive = DateTime.Now; - Prospects[client].Encryptor = new StreamCipher(privateKey); + Prospects[client].Encryptor = new BlockCipher(privateKey); Clients[client] = Prospects[client]; Prospects.Remove(client); } else @@ -92,7 +102,7 @@ namespace SockScape { if(!IsClientConnected(client) || packet.RegionCount < 1) break; - if(packet.CheckRegions(0, 1)) { + if(!packet.CheckRegions(0, 1)) { NegativeAck(endPoint, encryptor, kIntraSlaveId.StatusUpdate, "Server count is malformed."); break; } @@ -104,14 +114,15 @@ namespace SockScape { } for(byte i = 0; i < serverCount; ++i) { - if(!packet.CheckRegions(2 + 3 * i, 2, 2, 2)) + if(!packet.CheckRegions(1 + 3 * i, 2, 2, 2)) continue; MasterServerList.Write(new Server { - Id = packet[2 + 3 * i].Raw.UnpackUInt16(), - UserCount = packet[3 + 3 * i].Raw.UnpackUInt16(), + Id = packet[1 + 3 * i].Raw.UnpackUInt16(), + UserCount = packet[2 + 3 * i].Raw.UnpackUInt16(), Address = endPoint.Address, - Port = packet[4 + 3 * i].Raw.UnpackUInt16() + Port = packet[3 + 3 * i].Raw.UnpackUInt16(), + Owner = Clients[client] }); } @@ -120,7 +131,12 @@ namespace SockScape { } } + Prospects = Prospects.Where(x => x.Value.ReceiveDelta.Seconds < 10) + .ToDictionary(x => x.Key, x => x.Value); + var expiredClients = Clients.Where(x => x.Value.ReceiveDelta.Seconds > 60).Select(x => x.Value).ToList(); + if(expiredClients.Count > 0) + MasterServerList.RemoveServersByOwners(expiredClients); Thread.Sleep(1); } @@ -149,23 +165,23 @@ namespace SockScape { return Clients.ContainsKey(client); } - private static void PositiveAck(IPEndPoint endPoint, StreamCipher cipher, kIntraSlaveId id) { - Send(cipher.Parse(new Packet(kIntraMasterId.PositiveAck, id).GetBytes()), endPoint); + private static void PositiveAck(IPEndPoint endPoint, BlockCipher cipher, kIntraSlaveId id) { + Send(cipher.Encrypt(new Packet(kIntraMasterId.PositiveAck, (byte)id).GetBytes()), endPoint); } - private static void NegativeAck(IPEndPoint endPoint, StreamCipher cipher, kIntraSlaveId id, string message = "An error occurred while parsing a packet.") { - Send(cipher.Parse(new Packet(kIntraMasterId.NegativeAck, id, message).GetBytes()), endPoint); + private static void NegativeAck(IPEndPoint endPoint, BlockCipher cipher, kIntraSlaveId id, string message = "An error occurred while parsing a packet.") { + Send(cipher.Encrypt(new Packet(kIntraMasterId.NegativeAck, (byte)id, message).GetBytes()), endPoint); } private static void EncryptionError(IPEndPoint endPoint, string message = "A general encryption error has occurred. Renegotiation required.") { Send(new Packet(kIntraMasterId.EncryptionError, message), endPoint); } - class Client { + public class Client { public IPEndPoint Address { get; set; } public DateTime LastReceive { get; set; } public TimeSpan ReceiveDelta => DateTime.Now - LastReceive; - public StreamCipher Encryptor { get; set; } + public BlockCipher Encryptor { get; set; } public Key Key { get; set; } } } diff --git a/server/Socks/Protocols/Packet.cs b/server/Socks/Protocols/Packet.cs index 296cd2a..80b375c 100644 --- a/server/Socks/Protocols/Packet.cs +++ b/server/Socks/Protocols/Packet.cs @@ -88,6 +88,10 @@ namespace SockScape { Regions.Add((byte[])region); else if(region.GetType() == typeof(string)) Regions.Add(((string)region).GetBytes()); + else if(region.GetType() == typeof(byte)) + Regions.Add(new[] { (byte)region }); + else + Console.WriteLine($"Could not add region {region} of type {region.GetType()}."); return this; } @@ -104,7 +108,7 @@ namespace SockScape { return false; for(int i = 0; i < lengths.Length; ++i) { - if(this[startIndex + i].Raw.Length == lengths[i]) + if(this[startIndex + i].Raw.Length != lengths[i]) return false; }