diff --git a/protocol.md b/protocol.md index 847d676..eae0c43 100644 --- a/protocol.md +++ b/protocol.md @@ -283,19 +283,31 @@ Communication between the master server and clients will be done over a WebSocke 2 Message String - R1 + ¬R1 2 Session Id Packed Unsigned Long - ¬R1 + R1 3 Secret Bytes (16) - ¬R1 + R1 + + + 4 + Server Address + IPv4 String + R1 + + + 5 + Server Port + Packed Unsigned Short + R1 @@ -345,25 +357,15 @@ Communication between the master server and clients will be done over a WebSocke Iterated over n (0 ≤ in - 1) - 2 + 4i + 2 + 2i Server Id Packed Unsigned Short - 3 + 4i + 3 + 2i User Count Packed Unsigned Short - - 4 + 4i - IP Address - IPv4 String - - - 5 + 4i - Port - Packed Unsigned Short - #### Client to Master @@ -409,6 +411,11 @@ Communication between the master server and clients will be done over a WebSocke Password String + + 3 + Server Id + Packed Unsigned Short + diff --git a/server/DAL/Session.cs b/server/DAL/Session.cs index c49767e..cf867ef 100644 --- a/server/DAL/Session.cs +++ b/server/DAL/Session.cs @@ -17,6 +17,13 @@ namespace SockScape.DAL { [Required, Index(IsUnique = true)] public byte[] Secret { get; set; } + [Required] + public DateTime LastPing { get; set; } + + [NotMapped] + public TimeSpan DeltaLastPing + => DateTime.UtcNow - LastPing; + [Required] public int ServerId { get; set; } } diff --git a/server/Libraries/Kneesocks/Connection.cs b/server/Libraries/Kneesocks/Connection.cs index c8ad887..241c7be 100644 --- a/server/Libraries/Kneesocks/Connection.cs +++ b/server/Libraries/Kneesocks/Connection.cs @@ -171,9 +171,9 @@ namespace Kneesocks { internal void Parse() { if(Handshaked) { if(!Buffer.IsReading) { - if(TimeSinceLastPing.Seconds > TimeoutInterval) { + if(TimeSinceLastPing.TotalSeconds > TimeoutInterval) { Disconnect(Frame.kClosingReason.Normal, "Ping response timed out."); - } else if(TimeSinceLastPing.Seconds > PingInterval && !AwaitingPingResponse) { + } else if(TimeSinceLastPing.TotalSeconds > PingInterval && !AwaitingPingResponse) { var frameBytes = new Frame { IsFinal = true, IsMasked = false, @@ -204,7 +204,7 @@ namespace Kneesocks { if(Buffer.IsReading) { readBuffer = Buffer.AttemptRead(); if(readBuffer == null) { - if((!Handshaked || (Handshaked && FirstTwoBytes != null)) && Buffer.ElapsedReadTime.Seconds > (Handshaked ? 300 : 30)) + if((!Handshaked || (Handshaked && FirstTwoBytes != null)) && Buffer.ElapsedReadTime.TotalSeconds > (Handshaked ? 300 : 30)) Disconnect(Frame.kClosingReason.ProtocolError, "Timed out waiting for a full response"); return; diff --git a/server/MasterServerList.cs b/server/MasterServerList.cs index c1cc0d1..28af288 100644 --- a/server/MasterServerList.cs +++ b/server/MasterServerList.cs @@ -36,8 +36,7 @@ namespace SockScape { 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()); + packet.AddRegions(server.Key.Pack(), server.Value.UserCount.Pack()); return packet; } @@ -47,7 +46,7 @@ namespace SockScape { public static void RemoveServersByOwners(IEnumerable owners) { lock(_Servers) { _Servers = _Servers.Where(x => !owners.Contains(x.Value.Owner)) - .ToDictionary(x => x.Key, x => x.Value); + .ToDictionary(x => x.Key, x => x.Value); } } diff --git a/server/Socks/MasterConnection.cs b/server/Socks/MasterConnection.cs index 91afbc0..99ab89d 100644 --- a/server/Socks/MasterConnection.cs +++ b/server/Socks/MasterConnection.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data.Entity; using System.Linq; using System.Net; using System.Text; @@ -16,7 +17,7 @@ namespace SockScape { protected override void OnOpen() { Key = new Key(); - Send(Key.GenerateRequestPacket().GetBytes()); + Send(Key.GenerateRequestPacket()); } protected override void OnParse() { @@ -49,11 +50,44 @@ namespace SockScape { Encryptor = new StreamCipher(Key.PrivateKey); break; case kInterMasterId.LoginAttempt: - if(packet.RegionCount != 2) + if(packet.RegionCount != 3 || !packet.CheckRegions(2, 2)) break; using(var db = new ScapeDb()) { - if(db.Users.) + User user; + if((user = db.Users.FirstOrDefault(x => x.Username == packet[0])) == null) { + SendEncrypted(new Packet(kInterMasterId.LoginAttempt, Convert.ToByte(false), "User does not exist.")); + break; + } + + if(!packet[1].Str.CheckPassword(user.Password)) { + SendEncrypted(new Packet(kInterMasterId.LoginAttempt, Convert.ToByte(false), "Password is incorrect.")); + break; + } + + if(user.Session?.DeltaLastPing.TotalMinutes < 3) { + SendEncrypted(new Packet(kInterMasterId.LoginAttempt, Convert.ToByte(false), "You are already logged in. Log out or try again shortly.")); + break; + } + + ushort server = packet[2].Raw.UnpackUInt16(); + if(!MasterServerList.HasId(server)) { + SendEncrypted(new Packet(kInterMasterId.LoginAttempt, Convert.ToByte(false), "The world you have specified is offline.")); + break; + } + + if(user.Session?.DeltaLastPing.TotalMinutes >= 3) { + db.Entry(user.Session).State = EntityState.Deleted; + db.SaveChanges(); + } + + db.Sessions.Add(new Session { + Id = user.Id, + Secret = RNG.NextBytes(16), + ServerId = server, + LastPing = DateTime.UtcNow + }); + db.SaveChanges(); } break; case kInterMasterId.RegistrationAttempt: @@ -68,5 +102,13 @@ namespace SockScape { Console.WriteLine($"{Id} says {data.GetString()}"); } + + private void Send(Packet packet) { + Send(packet.GetBytes()); + } + + private void SendEncrypted(Packet packet) { + Send(Encryptor.Parse(packet.GetBytes())); + } } } diff --git a/server/Socks/MasterIntraClient.cs b/server/Socks/MasterIntraClient.cs index ca1a0c1..c980d42 100644 --- a/server/Socks/MasterIntraClient.cs +++ b/server/Socks/MasterIntraClient.cs @@ -20,11 +20,11 @@ namespace SockScape { private static DateTime LastMessageOut = new DateTime(0); private static TimeSpan DeltaLastOut - => DateTime.Now - LastMessageOut; + => DateTime.UtcNow - LastMessageOut; private static DateTime LastMessageIn = new DateTime(0); private static TimeSpan DeltaLastIn - => DateTime.Now - LastMessageIn; + => DateTime.UtcNow - LastMessageIn; public static void Initialize() { if(IsOpen || ListeningThread != null) @@ -52,7 +52,7 @@ namespace SockScape { var endPoint = new IPEndPoint(IPAddress.Any, 0); while(Sock.Available > 0) { var data = Sock.Receive(ref endPoint); - LastMessageIn = DateTime.Now; + LastMessageIn = DateTime.UtcNow; bool readRaw = Encryptor == null || data.Take(Packet.MagicNumber.Length).SequenceEqual(Packet.MagicNumber); @@ -108,7 +108,7 @@ namespace SockScape { public static void Send(byte[] bytes) { Sock.Send(bytes, bytes.Length); - LastMessageOut = DateTime.Now; + LastMessageOut = DateTime.UtcNow; } public static void Close() { diff --git a/server/Socks/MasterIntraServer.cs b/server/Socks/MasterIntraServer.cs index 0856164..d253134 100644 --- a/server/Socks/MasterIntraServer.cs +++ b/server/Socks/MasterIntraServer.cs @@ -63,9 +63,9 @@ namespace SockScape { } if(IsProspectConnected(client) && encryptor == null) - Prospects[client].LastReceive = DateTime.Now; + Prospects[client].LastReceive = DateTime.UtcNow; else if(IsClientConnected(client) && encryptor != null) - Clients[client].LastReceive = DateTime.Now; + Clients[client].LastReceive = DateTime.UtcNow; switch((kIntraSlaveId)packet.Id) { case kIntraSlaveId.InitiationAttempt: @@ -75,7 +75,7 @@ namespace SockScape { if(packet[0] == Configuration.General["Master Secret"]) { var key = new Key(); Prospects[client] = new Client { - LastReceive = DateTime.Now, + LastReceive = DateTime.UtcNow, Address = endPoint, Key = key }; @@ -90,7 +90,7 @@ namespace SockScape { var privateKey = Prospects[client].Key.ParseResponsePacket(packet); if(privateKey != -1) { - Prospects[client].LastReceive = DateTime.Now; + Prospects[client].LastReceive = DateTime.UtcNow; Prospects[client].Encryptor = new BlockCipher(privateKey); Clients[client] = Prospects[client]; Prospects.Remove(client); @@ -131,10 +131,10 @@ namespace SockScape { } } - Prospects = Prospects.Where(x => x.Value.ReceiveDelta.Seconds < 10) + Prospects = Prospects.Where(x => x.Value.ReceiveDelta.TotalSeconds < 10) .ToDictionary(x => x.Key, x => x.Value); - var expiredClients = Clients.Where(x => x.Value.ReceiveDelta.Seconds > 60).Select(x => x.Value).ToList(); + var expiredClients = Clients.Where(x => x.Value.ReceiveDelta.TotalSeconds > 60).Select(x => x.Value).ToList(); if(expiredClients.Count > 0) MasterServerList.RemoveServersByOwners(expiredClients); @@ -180,7 +180,7 @@ namespace SockScape { public class Client { public IPEndPoint Address { get; set; } public DateTime LastReceive { get; set; } - public TimeSpan ReceiveDelta => DateTime.Now - LastReceive; + public TimeSpan ReceiveDelta => DateTime.UtcNow - LastReceive; public BlockCipher Encryptor { get; set; } public Key Key { get; set; } } diff --git a/server/Socks/PlayerConnection.cs b/server/Socks/PlayerConnection.cs index 567548c..4bce3b4 100644 --- a/server/Socks/PlayerConnection.cs +++ b/server/Socks/PlayerConnection.cs @@ -17,7 +17,7 @@ namespace SockScape { } protected override void OnParse() { - if((DateTime.UtcNow - ConnectionOpened).Seconds > 60) { + if((DateTime.UtcNow - ConnectionOpened).TotalSeconds > 60) { Disconnect(Frame.kClosingReason.ProtocolError, "Logon request timed out."); } }