using Quasar.Common.Cryptography; using Quasar.Common.Messages; using System; using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; namespace Quasar.Server.Networking { public class QuasarServer : Server { /// /// Gets the clients currently connected and identified to the server. /// public Client[] ConnectedClients { get { return Clients.Where(c => c != null && c.Identified).ToArray(); } } /// /// Occurs when a client connected. /// public event ClientConnectedEventHandler ClientConnected; /// /// Represents the method that will handle the connected client. /// /// The connected client. public delegate void ClientConnectedEventHandler(Client client); /// /// Fires an event that informs subscribers that the client is connected. /// /// The connected client. private void OnClientConnected(Client client) { if (ProcessingDisconnect || !Listening) return; var handler = ClientConnected; handler?.Invoke(client); } /// /// Occurs when a client disconnected. /// public event ClientDisconnectedEventHandler ClientDisconnected; /// /// Represents the method that will handle the disconnected client. /// /// The disconnected client. public delegate void ClientDisconnectedEventHandler(Client client); /// /// Fires an event that informs subscribers that the client is disconnected. /// /// The disconnected client. private void OnClientDisconnected(Client client) { if (ProcessingDisconnect || !Listening) return; var handler = ClientDisconnected; handler?.Invoke(client); } /// /// Constructor, initializes required objects and subscribes to events of the server. /// /// The server certificate. public QuasarServer(X509Certificate2 serverCertificate) : base(serverCertificate) { base.ClientState += OnClientState; base.ClientRead += OnClientRead; } /// /// Decides if the client connected or disconnected. /// /// The server the client is connected to. /// The client which changed its state. /// True if the client connected, false if disconnected. private void OnClientState(Server server, Client client, bool connected) { if (!connected) { if (client.Identified) { OnClientDisconnected(client); } } } /// /// Forwards received messages from the client to the MessageHandler. /// /// The server the client is connected to. /// The client which has received the message. /// The received message. private void OnClientRead(Server server, Client client, IMessage message) { if (!client.Identified) { if (message.GetType() == typeof (ClientIdentification)) { client.Identified = IdentifyClient(client, (ClientIdentification) message); if (client.Identified) { client.Send(new ClientIdentificationResult {Result = true}); // finish handshake OnClientConnected(client); } else { // identification failed client.Disconnect(); } } else { // no messages of other types are allowed as long as client is in unidentified state client.Disconnect(); } return; } MessageHandler.Process(client, message); } private bool IdentifyClient(Client client, ClientIdentification packet) { if (packet.Id.Length != 64) return false; client.Value.Version = packet.Version; client.Value.OperatingSystem = packet.OperatingSystem; client.Value.AccountType = packet.AccountType; client.Value.Country = packet.Country; client.Value.CountryCode = packet.CountryCode; client.Value.Id = packet.Id; client.Value.Username = packet.Username; client.Value.PcName = packet.PcName; client.Value.Tag = packet.Tag; client.Value.ImageIndex = packet.ImageIndex; client.Value.EncryptionKey = packet.EncryptionKey; // TODO: Refactor tooltip //if (Settings.ShowToolTip) // client.Send(new GetSystemInfo()); #if !DEBUG try { var csp = (RSACryptoServiceProvider)ServerCertificate.PublicKey.Key; return csp.VerifyHash(Sha256.ComputeHash(Encoding.UTF8.GetBytes(packet.EncryptionKey)), CryptoConfig.MapNameToOID("SHA256"), packet.Signature); } catch (Exception) { return false; } #else return true; #endif } } }