| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 | using System.Formats.Asn1;using System.IO;using System.Reflection;using System.Security.Cryptography.X509Certificates;using InABox.Clients;using InABox.Core;using InABox.IPC;using InABox.Logging;using InABox.Rpc;using PRSServer;namespace PRSServices;public interface IEngine{    string ServiceName { get; set; }    string Version { get; set; }    void Run();    void Stop();    void Configure(Server settings);    PortStatus[] PortStatusList();}public abstract class Engine<TProperties> : IEngine where TProperties : ServerProperties{        private RpcServerPipeTransport _enginemanager;        protected  RpcClientPipeTransport? Transport;    public TProperties Properties { get; private set; }    public abstract void Run();    public abstract void Stop();    public virtual PortStatus[] PortStatusList()    {        return new PortStatus[] { };    }    public string ServiceName { get; set; }    public string Version { get; set; }    protected string AppDataFolder { get; set; }    public virtual void Configure(Server server)    {        Properties = server.Properties as TProperties;        AppDataFolder = GetPath(server.Key);        MainLogger.AddLogger(new LogFileLogger(AppDataFolder));        MainLogger.AddLogger(new NamedPipeLogger(server.Key));                _enginemanager = new RpcServerPipeTransport($"{ServiceName}M");        _enginemanager.AddHandler<IEngine, PortStatusCommand, PortStatusParameters, PortStatusResult>(new PortStatusHandler(this));        _enginemanager.AfterMessage += (transport, args) => MainLogger.Send(LogType.Information,"",$"Engine Manager Message: {args.Message?.Command}");        _enginemanager.Start();    }    private bool _connecting = false;    protected void CheckConnection()    {        if (_connecting)        {            return;        }        _connecting = true;        // Wait for server connection        while (_connecting)        {            Logger.Send(LogType.Information, "", "Connecting to Database server...");            if (Client.Ping())                _connecting = false;            else            {                Logger.Send(LogType.Error, "", "Database server unavailable. Trying again in 30 seconds...");                Task.Delay(30_000).Wait();            }        }                ClientFactory.SetBypass();        return;    }    /// <summary>    /// Initialise the client to the database server, and set up reconnection loop.    /// </summary>    protected void InitialiseConnection(string serverKey, Platform clientPlatform)    {        Transport = new RpcClientPipeTransport(DatabaseServerProperties.GetPipeName(serverKey, true));        ClientFactory.SetClientType(typeof(RpcClient<>), clientPlatform, Version, Transport);        Transport.OnClose += Transport_OnClose;        CheckConnection();    }    private void Transport_OnClose(IRpcTransport transport, RpcTransportCloseArgs e)    {        // Try to reconnect when lost connection.        Logger.Send(LogType.Error, "", "Database server connection lost.");        CheckConnection();    }    public static string GetPath(string key)    {        if (Assembly.GetEntryAssembly() != null)            return Path.Combine(                Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),                Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location),                key            );        return Path.Combine(            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),            Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location),            key        );    }        public static X509Certificate2? LoadCertificate(string filename)    {        if (!String.IsNullOrWhiteSpace(filename))        {            Logger.Send(LogType.Information, "", "Certificate FileName is {0}", filename);            if (File.Exists(filename))            {                Logger.Send(LogType.Information, "", "Certificate found; verifying HTTPS Certificate");                try                {                    var certificate = new X509Certificate2(filename);                    if (certificate.NotAfter > DateTime.Now)                    {                                                var names = GetDnsNames(certificate);                        Logger.Send(LogType.Information, "", $"Certificate valid for {string.Join(',', names)}");                        return certificate;                    }                    else                    {                        Logger.Send(LogType.Error, "", "HTTPS Certificate has expired, using HTTP instead");                    }                }                catch (Exception)                {                    Logger.Send(LogType.Error, "", "Error validating HTTPS Certificate, using HTTP instead");                }            }            else                Logger.Send(LogType.Error, "", "Certificate File does not exist!");        }        return null;    }    public static IEnumerable<string> GetDnsNames(X509Certificate2 certificate)    {        yield return certificate.GetNameInfo(X509NameType.DnsName, false);        foreach (var name in DnsAlternateNames(certificate))            yield return name;    }    public static IEnumerable<string> DnsAlternateNames(X509Certificate2 certificate)    {        // Adapted from https://stackoverflow.com/questions/16698307/how-do-you-parse-the-subject-alternate-names-from-an-x509certificate2/59382929#59382929        // OID for SubjectAlternativeName X509 extension.        const string SAN_OID = "2.5.29.17";        var extension = certificate.Extensions[SAN_OID];        if (extension is null) yield break;        // Tag value "2" is defined by:        //        //    dNSName                         [2]     IA5String,        //        // in: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6        var dnsNameTag = new Asn1Tag(TagClass.ContextSpecific, tagValue: 2, isConstructed: false);        var asnReader = new AsnReader(extension.RawData, AsnEncodingRules.BER);        var sequenceReader = asnReader.ReadSequence(Asn1Tag.Sequence);        while (sequenceReader.HasData)        {            var tag = sequenceReader.PeekTag();            if(tag != dnsNameTag)            {                sequenceReader.ReadEncodedValue();                continue;            }            var dnsName = sequenceReader.ReadCharacterString(UniversalTagNumber.IA5String, dnsNameTag);            yield return dnsName;        }    }}
 |