123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- using H.Pipes;
- using H.Pipes.AccessControl;
- using H.Pipes.Args;
- using InABox.Core;
- using InABox.IPC;
- using InABox.Rpc;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.IO.Pipes;
- using System.Linq;
- using System.Security.Principal;
- using System.Text;
- using System.Threading.Tasks;
- namespace InABox.Server;
- internal class InternalServerSession : IRpcSession
- {
- public Guid ID { get; set; }
- public Platform Platform { get; set; }
- public string? Version { get; set; }
- public string UserID { get; set; }
- public Guid UserGuid { get; set; }
- public PipeConnection<RpcMessage?> Connection { get; set; }
- }
- /// <summary>
- /// This is the main server the database engine runs, which the proxies connect to.
- /// </summary>
- public class InternalServer : IPusher, IRpcServer
- {
- private PipeServer<RpcMessage?> _transport;
- public event LogEvent? OnLog;
- public bool IsSecure() => false;
- public InternalServer(string name)
- {
- _transport = new PipeServer<RpcMessage?>(name);
- #if WINDOWS
- SetPipeSecurity();
- #endif
-
- _transport.ClientConnected += Transport_OnConnected;
- _transport.ClientDisconnected += Transport_OnDisconnected;
- _transport.MessageReceived += Transport_OnMessage;
- _transport.ExceptionOccurred += Transport_OnException;
- AddHandler(new OpenSessionHandler(this));
- AddHandler(new CloseSessionHandler(this));
- AddHandler(new RpcPingHandler(this));
- AddHandler(new RpcInfoHandler(this));
- AddHandler(new RpcValidateHandler(this));
- AddHandler(new RpcCheck2FAHandler(this));
- AddHandler(new RpcQueryHandler(this));
- AddHandler(new RpcSaveHandler(this));
- AddHandler(new RpcDeleteHandler(this));
- AddHandler(new RpcVersionHandler(this));
- AddHandler(new RpcInstallerHandler(this));
- AddHandler(new RpcReleaseNotesHandler(this));
- }
- #region Handlers
- private Dictionary<string, IRpcCommandHandler> _handlers = new Dictionary<string, IRpcCommandHandler>();
- public void AddHandler<TSender, TCommand, TProperties, TResult>(RpcCommandHandler<TSender, TCommand, TProperties, TResult> handler)
- where TSender : class
- where TCommand : IRpcCommand<TProperties, TResult>
- where TProperties : IRpcCommandParameters, new()
- where TResult : IRpcCommandResult, new()
- {
- _handlers[typeof(TCommand).Name] = handler;
- }
- #endregion
- #region Sessions
- private ConcurrentDictionary<Guid, InternalServerSession> _sessions = new ConcurrentDictionary<Guid, InternalServerSession>();
- private class OpenSessionHandler : RpcCommandHandler<InternalServer, OpenSessionCommand, OpenSessionParameters, OpenSessionResult>
- {
- public OpenSessionHandler(InternalServer server) : base(server)
- {
- }
- protected override OpenSessionResult Execute(IRpcSession session, OpenSessionParameters parameters)
- {
- return new OpenSessionResult
- {
- SessionID = session.ID
- };
- }
- }
- private class CloseSessionHandler : RpcCommandHandler<InternalServer, CloseSessionCommand, CloseSessionParameters, CloseSessionResult>
- {
- public CloseSessionHandler(InternalServer server) : base(server)
- {
- }
- protected override CloseSessionResult Execute(IRpcSession session, CloseSessionParameters parameters)
- {
- Sender.DeleteSession(session.ID);
- return new CloseSessionResult();
- }
- }
- private InternalServerSession GetSession(Guid sessionID, PipeConnection<RpcMessage?> connection)
- {
- if(sessionID == Guid.Empty)
- {
- sessionID = Guid.NewGuid();
- }
- if(!_sessions.TryGetValue(sessionID, out var session))
- {
- session = new InternalServerSession
- {
- ID = sessionID,
- Connection = connection
- };
- _sessions.TryAdd(sessionID, session);
- }
- return session;
- }
- private InternalServerSession? DeleteSession(Guid sessionID)
- {
- _sessions.Remove(sessionID, out InternalServerSession? session);
- return session;
- }
- #endregion
- #region Transport Stuff
- private void SetPipeSecurity()
- {
- #pragma warning disable CA1416
-
- var pipeSecurity = new PipeSecurity();
- pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.LocalSid, null), PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow));
- pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.LocalServiceSid, null), PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow));
- pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null), PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow));
- _transport.SetPipeSecurity(pipeSecurity);
-
- #pragma warning restore CA1416
- }
- public void Start()
- {
- _transport.StartAsync().Wait();
- }
- public void Stop()
- {
- _transport.StopAsync().Wait();
- }
-
- private void Transport_OnConnected(object? sender, H.Pipes.Args.ConnectionEventArgs<RpcMessage?> e)
- {
- OnLog?.Invoke(LogType.Information, "", $"Client Connected");
- }
- private void Transport_OnMessage(object? sender, H.Pipes.Args.ConnectionMessageEventArgs<RpcMessage?> e)
- {
- Task.Run(() =>
- {
- var response = DoMessage(e.Connection, e.Message);
- e.Connection.WriteAsync(response);
- });
- }
- private void Transport_OnDisconnected(object? sender, H.Pipes.Args.ConnectionEventArgs<RpcMessage?> e)
- {
- OnLog?.Invoke(LogType.Information, "", $"Client Disconnected");
- foreach(var session in _sessions.Where(x => x.Value.Connection == e.Connection))
- {
- DeleteSession(session.Key);
- }
- e.Connection.DisposeAsync();
- }
- private void Transport_OnException(object? sender, H.Pipes.Args.ExceptionEventArgs e)
- {
- DoException(null, e.Exception);
- }
-
- protected void DoException(IRpcSession? session, Exception e)
- {
- if ((session?.ID ?? Guid.Empty) != Guid.Empty)
- OnLog?.Invoke(LogType.Error, $"", $"Exception Occurred in {session?.ID}: {e.Message}");
- }
- #endregion
- /// <summary>
- /// Handle a message from a client.
- /// </summary>
- /// <param name="connection">The client connection.</param>
- /// <param name="message">The message to be handled.</param>
- /// <returns>The response to be sent back to the client.</returns>
- public RpcMessage? DoMessage(PipeConnection<RpcMessage?>? connection, RpcMessage? message)
- {
- if(message is null)
- {
- DoException(null, new Exception("NULL Message Received"));
- return null;
- }
- if(connection is null)
- {
- DoException(null, new Exception("NULL connection"));
- return null;
- }
- var response = new RpcMessage() { Id = message.Id, Command = message.Command };
- try
- {
- var data = Serialization.ReadBinary<InternalServerMessage>(message.Payload, BinarySerializationSettings.Latest);
- var session = GetSession(data.Session, connection);
- response = new RpcMessage() { Id = message.Id, Command = message.Command };
- if (_handlers.TryGetValue(message.Command, out var command))
- {
- try
- {
- response.Payload = command.Execute(session, data.Payload);
- }
- catch (RpcException err)
- {
- response.Payload = Encoding.UTF8.GetBytes(err.Message);
- response.Error = err.Error;
- }
- }
- else
- {
- DoException(session, new Exception("Command Not Found"));
- response.Error = RpcError.COMMANDNOTFOUND;
- }
- }
- catch(Exception e)
- {
- DoException(null, e);
- response.Payload = Encoding.UTF8.GetBytes(e.Message);
- response.Error = RpcError.SERVERERROR;
- }
- return response;
- }
- /// <summary>
- /// Send a message to a particular client connection.
- /// </summary>
- /// <param name="connection">The connection to send to.</param>
- /// <param name="message">The message to send.</param>
- public void Send(PipeConnection<RpcMessage?> connection, RpcMessage message)
- {
- connection.WriteAsync(message);
- }
- #region Pusher Stuff
- public void PushToAll<TPush>(TPush push) where TPush : BaseObject
- {
- var internalMessage = new InternalServerMessage
- {
- Session = Guid.Empty,
- Payload = RpcPush.Create(push).WriteBinary(BinarySerializationSettings.Latest)
- };
- var message = new RpcMessage(Guid.NewGuid(), "Push", internalMessage.WriteBinary(BinarySerializationSettings.Latest));
- foreach (var connection in _transport.ConnectedClients)
- {
- Send(connection, message);
- }
- }
- public void PushToSession<TPush>(Guid session, TPush push) where TPush : BaseObject
- {
- var internalMessage = new InternalServerMessage
- {
- Session = session,
- Payload = RpcPush.Create(push).WriteBinary(BinarySerializationSettings.Latest)
- };
- var message = new RpcMessage(Guid.NewGuid(), "Push", internalMessage.WriteBinary(BinarySerializationSettings.Latest));
- var sessionConnection = _sessions.FirstOrDefault(x => x.Value.ID == session).Value.Connection;
- if(sessionConnection is not null)
- {
- Send(sessionConnection, message);
- }
- }
- public void PushToSession(Guid session, Type TPush, BaseObject push)
- {
- var internalMessage = new InternalServerMessage
- {
- Session = session,
- Payload = new RpcPush
- {
- Object = push,
- Type = TPush
- }.WriteBinary(BinarySerializationSettings.Latest)
- };
- var message = new RpcMessage(Guid.NewGuid(), "Push", internalMessage.WriteBinary(BinarySerializationSettings.Latest));
- var sessionConnection = _sessions.FirstOrDefault(x => x.Value.ID == session).Value.Connection;
- if (sessionConnection is not null)
- {
- Send(sessionConnection, message);
- }
- }
- public IEnumerable<Guid> GetUserSessions(Guid user) =>
- _sessions.Values.Where(x => x.UserGuid == user).Select(x => x.ID);
- public IEnumerable<Guid> GetSessions(Platform platform) =>
- _sessions.Values.Where(x => x.Platform == platform).Select(x => x.ID);
- #endregion
- public void Dispose()
- {
- _transport.DisposeAsync().AsTask().Wait();
- }
-
- ~InternalServer()
- {
- Dispose();
- }
- }
|